Flutter 튜토리얼 3편: Flutter를 위한 Dart 기초
요약
핵심 요지
문서가 설명하는 범위
- Flutter 개발에 필요한 Dart 핵심 개념
- Dart의 타입 시스템과 null safety
- 비동기 프로그래밍 기초
- Dart 학습 리소스와 권장 순서
읽는 시간: 12분 | 난이도: 초급
참고 자료
- Flutter Fundamentals - Flutter 기초 학습 가이드
- Intro to Dart - Flutter를 위한 Dart 소개
- Bootstrap into Dart - Dart 입문 리소스
문제 상황
Flutter 앱을 만들려면 Dart 언어로 코드를 작성해야 합니다.
Dart를 모르면 Flutter 코드가 무슨 의미인지 이해할 수 없습니다.
그렇다면 Dart는 어떤 언어일까요?
Dart 없이 Flutter를 배우려는 시도
// 이 코드가 무슨 뜻인지 모르면 Flutter를 배울 수 없다class MyApp extends StatelessWidget { const MyApp({super.key});
@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center(child: Text('Hello')), ), ); }}문제는 다음과 같습니다.
class,extends,const,@override등 키워드의 의미를 모른다.Widget build(BuildContext context)가 무슨 뜻인지 모른다.?와!같은 null safety 문법이 낯설다.async,await,Future같은 비동기 개념이 어렵다.
해결 방법
Dart는 Java, JavaScript, C++과 비슷한 문법을 사용합니다.
중괄호 {}로 코드 블록을 만들고, 세미콜론 ;으로 문장을 끝내는 방식이죠.
다른 프로그래밍 언어를 배운 적이 있다면 금방 익숙해질 겁니다.
챕터 1: Dart 언어의 특징 이해하기
Why
NOTEFlutter가 왜 Dart를 선택했는지 알면 학습 동기가 생깁니다.
Dart는 UI 개발에 최적화된 언어입니다.
왜 그럴까요?Dart의 장점├── 빠른 개발: hot reload 지원├── 빠른 실행: AOT 컴파일로 네이티브 성능├── 타입 안전: 컴파일 타임 오류 검출└── null safety: 런타임 null 오류 방지
What
NOTEDart는 Google이 개발한 프로그래밍 언어입니다.
스마트폰 앱처럼 사용자가 직접 만지는 프로그램을 만들기 좋게 설계되었습니다.
문법은 Java나 JavaScript와 비슷해서 배우기 쉽습니다.
How
TIPDart의 핵심 특징
특징 설명 예시 타입 안전 정적 타입 검사 int count = 0;타입 추론 타입 자동 결정 var name = 'Flutter';null safety null 가능성 명시 String? nullable;비동기 지원 async/await await fetchData();기본 문법 예시
// 변수 선언int count = 0; // 타입 명시var name = 'Flutter'; // 타입 추론final pi = 3.14; // 한 번만 할당const gravity = 9.8; // 컴파일 타임 상수// 함수 정의String greet(String name) {return 'Hello, $name!';}// 클래스 정의class Person {final String name;final int age;Person(this.name, this.age);void introduce() {print('저는 $name이고, $age살입니다.');}}
Watch out
WARNINGDart는 JavaScript와 비슷해 보이지만 중요한 차이점이 있습니다.
// JavaScript와 다른 점// 1. 세미콜론 필수var name = 'Dart'; // ✓ 세미콜론 있음// var name = 'Dart' // ✗ 오류// 2. 타입 안전int count = 0;// count = 'hello'; // ✗ 타입 오류// 3. single/double quote 동일var a = 'hello';var b = "hello"; // 둘 다 String
결론: Dart는 C 계열 언어 경험이 있다면 빠르게 익힐 수 있습니다.
챕터 2: null safety 이해하기
Why
NOTE
null3은 “값이 없음”을 나타냅니다.
문제는 null인 변수를 사용하려고 하면 프로그램이 멈춘다는 거예요.
Dart의 null safety는 이런 실수를 미리 잡아줍니다.// null safety가 없던 시절String name = null; // 허용됨print(name.length); // 런타임 오류!// null safety 적용 후String name = null; // 컴파일 오류!
What
NOTEnull safety는 변수가 null이 될 수 있는지를 미리 정하는 기능입니다.
String이라고 쓰면 “이 변수는 절대 null이 아니야”라는 뜻이고요.
String?처럼 물음표를 붙이면 “이 변수는 null일 수도 있어”라는 뜻입니다.
How
TIPnullable과 non-nullable
// non-nullable: null 불가String name = 'Flutter';// name = null; // 컴파일 오류// nullable: null 가능String? nickname; // 기본값 nullnickname = 'F';nickname = null; // OKnull 체크 방법
String? name = getName();// 방법 1: if 조건문if (name != null) {print(name.length); // name이 String으로 승격됨}// 방법 2: null-aware 연산자print(name?.length); // null이면 null 반환print(name ?? 'Unknown'); // null이면 기본값 사용print(name!.length); // null이 아님을 단언 (위험)클래스에서 null safety
class Package {final String name; // 필수 (non-nullable)final String version; // 필수 (non-nullable)final String? description; // 선택 (nullable)Package(this.name, this.version, {this.description});}// 사용var pkg = Package('flutter', '3.38.1');var pkg2 = Package('dart', '3.7.0', description: 'Dart SDK');
타입 null 가능 사용 시 체크 필요 String✗ 불필요 String?✓ 필요 int✗ 불필요 int?✓ 필요
Watch out
WARNING
!연산자는 null이 아님을 단언하지만, 실제로 null이면 런타임 오류가 발생합니다.String? name = null;print(name!.length); // 런타임 오류: Null check operator used on a null value// 안전한 방법if (name != null) {print(name.length);}// 또는print(name?.length ?? 0);
결론: null safety로 null 관련 오류를 컴파일 타임에 잡을 수 있습니다.
챕터 3: 비동기 프로그래밍 이해하기
Why
NOTE인터넷에서 데이터를 가져오거나 파일을 읽는 작업은 시간이 걸립니다.
만약 이 작업이 끝날 때까지 기다리면 어떻게 될까요?
앱이 멈춘 것처럼 보입니다.
그래서 비동기 프로그래밍이 필요합니다.// 동기 방식 (앱이 멈춤)var data = fetchDataSync(); // 3초 대기print(data); // 3초 후 실행// 비동기 방식 (앱이 멈추지 않음)fetchDataAsync().then((data) {print(data); // 데이터 준비되면 실행});// 즉시 다음 코드 실행
What
NOTE
Future4는 “나중에 결과가 올 거야”라는 약속입니다.
async와await를 사용하면 복잡한 비동기 코드를 간단하게 작성할 수 있습니다.
마치 순서대로 실행되는 코드처럼요.
How
TIPasync/await 사용법
// Future를 반환하는 함수Future<String> fetchUserName() async {// 네트워크 요청 시뮬레이션await Future.delayed(Duration(seconds: 1));return 'Flutter Developer';}// await로 결과 기다리기void main() async {print('요청 시작');String name = await fetchUserName();print('이름: $name');print('요청 완료');}// 출력:// 요청 시작// (1초 후)// 이름: Flutter Developer// 요청 완료HTTP 요청 예시
import 'package:http/http.dart' as http;import 'dart:convert';Future<Map<String, dynamic>> fetchUser(int id) async {final response = await http.get(Uri.parse('https://api.example.com/users/$id'),);if (response.statusCode == 200) {return jsonDecode(response.body);} else {throw Exception('Failed to load user');}}에러 처리
Future<void> loadData() async {try {final data = await fetchData();print(data);} catch (e) {print('오류 발생: $e');}}
Watch out
WARNING
await는async함수 안에서만 사용할 수 있습니다.// 잘못된 사용void main() {String name = await fetchUserName(); // 오류: await in non-async function}// 올바른 사용void main() async {String name = await fetchUserName(); // OK}// 또는 then 사용void main() {fetchUserName().then((name) {print(name);});}
결론: async/await로 비동기 작업을 동기 코드처럼 간결하게 작성할 수 있습니다.
챕터 4: 클래스와 생성자 이해하기
Why
NOTEFlutter의 모든 Widget은 클래스입니다.
클래스가 뭔지 모르면 Flutter 코드를 읽을 수 없습니다.
클래스는 생각보다 어렵지 않습니다.// Flutter Widget은 모두 클래스class MyWidget extends StatelessWidget {// ...}
What
NOTE클래스는 관련된 데이터와 기능을 하나로 묶은 것입니다.
예를 들어 “사람” 클래스에는 이름, 나이 같은 데이터와 인사하기 같은 기능이 있을 수 있죠.
Flutter의 Widget도 이런 클래스로 만들어져 있습니다.
How
TIP클래스 기본 구조
class Person {// 필드 (인스턴스 변수)String name;int age;// 생성자Person(this.name, this.age);// 메서드void introduce() {print('저는 $name이고 $age살입니다.');}}// 사용var person = Person('김철수', 30);person.introduce();명명된 매개변수 (Named Parameters)
class Button {final String text;final double width;final double height;// 명명된 매개변수 (중괄호 사용)Button({required this.text, // 필수this.width = 100, // 기본값this.height = 50, // 기본값});}// 사용 (순서 상관없음)var btn1 = Button(text: 'Click');var btn2 = Button(text: 'Submit', width: 200, height: 60);상속과 오버라이드
class Animal {void speak() {print('...');}}class Dog extends Animal {@overridevoid speak() {print('멍멍!');}}class Cat extends Animal {@overridevoid speak() {print('야옹!');}}Flutter Widget 예시
class MyButton extends StatelessWidget {final String label;final VoidCallback onPressed;const MyButton({super.key,required this.label,required this.onPressed,});@overrideWidget build(BuildContext context) {return ElevatedButton(onPressed: onPressed,child: Text(label),);}}
Watch out
WARNING
const생성자는 컴파일 타임 상수를 만들 때 사용합니다. 모든 필드가final이어야const생성자를 사용할 수 있습니다.class Point {final int x;final int y;const Point(this.x, this.y); // const 생성자}// const 인스턴스 (동일 값이면 같은 객체)const p1 = Point(0, 0);const p2 = Point(0, 0);print(identical(p1, p2)); // true// 일반 인스턴스 (항상 새 객체)var p3 = Point(0, 0);var p4 = Point(0, 0);print(identical(p3, p4)); // false
결론: 클래스와 생성자 문법을 알면 Flutter Widget 코드를 이해할 수 있습니다.
챕터 5: Flutter 앱의 진입점 이해하기
Why
NOTE모든 Dart 프로그램은
main()함수에서 시작합니다.
Flutter 앱도 마찬가지입니다.
main()에서runApp()을 호출하면 앱이 시작됩니다.void main() {runApp(const MyApp());}
What
NOTE
runApp()5은 Flutter 앱을 실행하는 함수입니다.
이 함수에 전달한 Widget이 앱의 첫 화면이 됩니다.
모든 Flutter 앱은 이렇게 시작합니다.
How
TIP기본 진입점 구조
import 'package:flutter/material.dart';void main() {runApp(const MyApp());}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'My App',home: HomePage(),);}}초기화 작업이 필요한 경우
void main() async {// Flutter 엔진 초기화 (비동기 작업 전 필수)WidgetsFlutterBinding.ensureInitialized();// 비동기 초기화 작업await Firebase.initializeApp();await loadSettings();runApp(const MyApp());}flowchart TD A[main 함수 시작] --> B{초기화 필요?} B -->|예| C[WidgetsFlutterBinding.ensureInitialized] C --> D[비동기 초기화 작업] D --> E[runApp 호출] B -->|아니오| E E --> F[Widget 트리 렌더링]
Watch out
WARNING비동기 초기화를 하려면
WidgetsFlutterBinding.ensureInitialized()를 먼저 호출해야 합니다.// 잘못된 순서void main() async {await Firebase.initializeApp(); // 오류 가능!runApp(const MyApp());}// 올바른 순서void main() async {WidgetsFlutterBinding.ensureInitialized(); // 먼저 호출await Firebase.initializeApp(); // 그 다음 초기화runApp(const MyApp());}
결론: main()과 runApp()이 Flutter 앱의 시작점입니다.
챕터 6: Dart 학습 리소스 활용하기
Why
NOTEDart 전문가가 될 필요는 없습니다.
하지만 기초 문법은 반드시 알아야 합니다.
어디서 배울 수 있을까요?
공식 문서가 가장 좋은 학습 자료입니다.
What
NOTEDart 공식 사이트에는 체계적인 학습 자료가 있습니다.
그중에서 Language Tour가 가장 좋은 시작점입니다.
Dart의 모든 문법을 예제와 함께 설명해줍니다.
How
TIP권장 학습 순서
순서 자료 내용 소요 시간 1 Language Tour Dart 문법 전체 2-3시간 2 Library Tour 핵심 라이브러리 1-2시간 3 Effective Dart 스타일 가이드 1시간 4 비동기 코드랩 Future, Stream 1시간 Language Tour에서 배울 것
- 변수와 타입
- 함수와 클래스
- null safety
- 제어문과 반복문
- 예외 처리
- 비동기 프로그래밍
JavaScript 개발자라면
JavaScript를 알고 있다면 더 빨리 배울 수 있습니다.
JavaScript to Dart에서 두 언어의 차이점을 확인해보세요.
Watch out
WARNINGDart를 완벽하게 배운 다음에 Flutter를 시작하려고 하지 마세요.
그러면 시간이 너무 오래 걸립니다.
기초 문법만 익히고 Flutter를 시작하세요.
필요할 때마다 Dart를 더 배우면 됩니다.권장 학습 흐름:1. Dart 기초 (변수, 함수, 클래스) - 2시간2. Flutter 시작 (첫 앱 만들기) - 1시간3. 필요할 때 Dart 추가 학습 - 지속
결론: Language Tour로 기초를 익히고, Flutter를 하면서 Dart를 더 배웁니다.
한계
이 문서는 Flutter 개발에 필요한 Dart 기초만 다룹니다.
더 깊은 내용은 별도로 학습이 필요합니다.
- 고급 Dart 기능: 제네릭, 믹스인, 익스텐션 등은 별도 학습이 필요합니다.
- Dart 전용 개발: CLI 앱, 서버 개발 등은 이 문서의 범위가 아닙니다.
- 심화 비동기: Stream, Isolate 등은 이후 튜토리얼에서 다룹니다.
Footnotes
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!