코딩 · 프로그래밍 언어
입문자를 위한 다트 기초 강좌
핵심 답변
다트(Dart)는 구글이 만든 정적 타입 객체 지향 언어로, Flutter 앱 프레임워크의 언어로 가장 잘 알려져 있습니다. 건전한 null 안전성을 갖추고 있고, 네이티브 코드와 JavaScript 또는 WebAssembly로 컴파일되며, 개발 중에는 빠른 핫 리로드를 지원합니다. 이 페이지에서는 환경 설정, 타입, null 안전성, 제어 흐름, 함수, 컬렉션, 클래스, 비동기 코드, 예외 처리를 차례로 살펴봅니다.
다트란 무엇이고 어디에 쓰는가
다트(Dart)는 구글이 개발한 클라이언트 최적화 프로그래밍 언어입니다. 타입 추론을 지원하는 정적 타입 언어이며 건전한 null 안전성을 갖추고 있고, 네이티브 기계어(모바일, 데스크톱, 서버용)와 JavaScript 또는 WebAssembly(웹용)로 모두 컴파일됩니다. 개발 중에는 핫 리로드가 되는 가상 머신에서 실행되므로 변경 사항이 실행 중인 앱에 몇 초 안에 반영됩니다.
대부분 Flutter로 앱을 만들려고 다트를 배웁니다. Flutter는 하나의 코드베이스로 Android, iOS, 웹, Windows, macOS, Linux를 지원하는 구글의 UI 툴킷입니다. 다트는 명령줄 도구와 백엔드 서비스에도 쓰입니다.
다트 설치와 Hello, world 실행
dart.dev에서 Dart SDK를 설치하세요(다트가 포함된 Flutter를 설치해도 됩니다). 모든 다트 프로그램은 최상위 main 함수에서 시작합니다.
// hello.dart
void main() {
print('Hello, world!');
}dart run hello.dart
# Hello, world!
dart compile exe hello.dart # builds a native executable
dart create my_app # creates a new console project이 페이지의 모든 예제는 dartpad.dev의 브라우저 기반 에디터인 DartPad에서도 실행해 볼 수 있습니다.
변수와 기본 타입
컴파일러가 타입을 추론하게 하려면 var, 한 번만 할당하는 변수에는 final, 컴파일 시점 상수에는 const를 사용합니다. 타입을 직접 명시해도 됩니다.
void main() {
var name = 'Dart'; // inferred as String
int year = 2011; // explicit type
final now = DateTime.now(); // set once, at run time
const pi = 3.14159; // compile-time constant
double ratio = 2; // int literal becomes 2.0
bool ok = year > 2000;
print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
// Dart since 2011, pi=3.14, ok=true, ratio=2.0
print(now.year > 2000); // true
}| 타입 | 예시 | 설명 |
|---|---|---|
int | 42 | 정수. 네이티브 플랫폼에서는 64비트 |
double | 3.14 | 64비트 부동 소수점 |
num | 7 또는 7.5 | int와 double의 상위 타입 |
bool | true | 조건식에는 true와 false만 쓸 수 있음 |
String | 'hi' | UTF-16. $x, ${expr} 보간 지원 |
dynamic | 무엇이든 가능 | 정적 검사를 끄므로 대부분의 코드에서는 피할 것 |
웹에서는 int 값이 JavaScript 숫자로 표현되므로 아주 큰 정수는 정밀도를 잃을 수 있습니다.
건전한 null 안전성
다트 3부터 모든 다트 코드는 건전한 null 안전성을 사용합니다. String 타입 변수는 null이 될 수 없으며, null을 허용하려면 String?라고 씁니다. 컴파일러가 모든 사용을 검사하므로 null 오류를 프로그램 실행 전에 잡아 냅니다.
int lengthOf(String? s) {
// return s.length; // compile error: s might be null
if (s == null) return 0;
return s.length; // promoted to String after the check
}
void main() {
String? nickname;
print(nickname?.length); // null (null-aware access)
print(nickname ?? 'guest'); // guest (default if null)
nickname ??= 'dee'; // assign only if null
print(lengthOf(nickname)); // 3
late String config; // initialized later, checked at run time
config = 'loaded';
print(config); // loaded
}제어 흐름
다트에는 일반적인 if, for, while, do-while이 있고, 모든 iterable을 순회하는 for-in 반복문도 있습니다. 다트 3부터 switch가 패턴 매칭을 지원하며 식으로도 사용할 수 있습니다.
void main() {
var scores = [72, 95, 48];
for (var s in scores) {
if (s >= 90) {
print('$s excellent');
} else if (s >= 50) {
print('$s pass');
} else {
print('$s fail');
}
}
for (var i = 0; i < 3; i++) {
print('i = $i');
}
var day = 6;
var kind = switch (day) {
6 || 7 => 'weekend',
>= 1 && <= 5 => 'weekday',
_ => 'invalid',
};
print(kind); // weekend
}함수
함수는 일급 객체입니다. 매개변수는 위치 매개변수, [] 안의 선택적 위치 매개변수, {} 안의 이름 있는 매개변수가 될 수 있습니다. 이름 있는 매개변수는 required를 붙이지 않으면 선택 사항입니다. 화살표 문법 =>는 식 하나를 반환하는 함수의 축약형입니다.
int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
var text = '$greeting, $name';
return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
width == null ? text : text.padRight(width, '.');
void main() {
print(add(2, 3)); // 5
print(greet('Ana', loud: false)); // Hello, Ana
print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
print(label('id', 5)); // id...
var twice = (int x) => x * 2; // anonymous function
print([1, 2, 3].map(twice).toList()); // [2, 4, 6]
}컬렉션: List, Set, Map, 레코드
핵심 컬렉션은 List(순서 있음), Set(중복 없음), Map(키-값 쌍)입니다. 컬렉션 리터럴에서는 if, for, 전개 연산자 ...를 쓸 수 있습니다. 다트 3에 추가된 레코드는 클래스를 선언하지 않고도 여러 값을 묶어 줍니다.
void main() {
var nums = [3, 1, 2]; // List<int>
nums.add(4);
nums.sort();
print(nums); // [1, 2, 3, 4]
var tags = {'dart', 'flutter', 'dart'};
print(tags.length); // 2
var ages = {'Ann': 31, 'Bo': 25}; // Map<String, int>
ages['Cy'] = 40;
print(ages['Bo']); // 25
var showAll = false;
var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
print(menu); // [Home, Item 1, Item 2, Item 3, Item 4]
(String, int) pair = ('Ann', 31); // a record
var (who, age) = pair; // destructuring
print('$who is $age'); // Ann is 31
}클래스와 async/await
다트는 객체 지향 언어이며 모든 값이 객체입니다. 클래스는 초기화 형식 매개변수(this.x)를 쓰는 생성자, 이름 있는 생성자, 게터, 믹스인, 인터페이스를 지원합니다. 비동기 코드는 async와 await와 함께 Future, Stream을 사용하며, 파일이나 네트워크를 기다리는 동안에도 Flutter 앱이 반응성을 유지하게 해 줍니다.
class Point {
final double x, y;
const Point(this.x, this.y);
Point.origin() : x = 0, y = 0; // named constructor
double get sum => x + y; // getter
@override
String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 100));
return 'ana';
}
Future<void> main() async {
print(Point(1, 2).sum); // 3.0
print(Point.origin()); // Point(0.0, 0.0)
var user = await fetchUser();
print('user: $user'); // user: ana
}예외 처리
throw로 예외를 던지고 try, on, catch, finally로 처리합니다. 특정 타입만 잡으려면 on SomeType을 사용하세요. 다트는 프로그램이 처리할 수 있는 예상된 문제인 Exception과, 보통 잡지 말아야 하는 버그(예: 실패한 assertion, 범위 오류)인 Error를 구분합니다.
int parseAge(String s) {
var n = int.tryParse(s); // null instead of throwing
if (n == null || n < 0) {
throw FormatException('Not a valid age: $s');
}
return n;
}
void main() {
for (var input in ['42', 'abc']) {
try {
print(parseAge(input));
} on FormatException catch (e) {
print('Error: ${e.message}');
} finally {
print('checked $input');
}
}
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abc작은 완성 프로그램: 장바구니 합계
이 프로그램은 클래스로 상품을 표현하고, 장바구니를 맵에 저장해 영수증을 출력합니다. 클래스, 맵, 문자열 서식, 컬렉션 메서드를 함께 사용합니다.
class Product {
final String name;
final double price;
const Product(this.name, this.price);
}
void main() {
const apple = Product('Apple', 0.5);
const bread = Product('Bread', 2.25);
final cart = <Product, int>{apple: 6, bread: 2};
var total = 0.0;
cart.forEach((product, qty) {
final line = product.price * qty;
total += line;
print('${product.name.padRight(6)} x$qty ${line.toStringAsFixed(2)}');
});
final count = cart.values.reduce((a, b) => a + b);
print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple x6 3.00
// Bread x2 4.50
// 8 items, total 7.50다음 단계
- dart.dev의 Dart 언어 투어와 튜토리얼.
- 공식 스타일·설계 가이드인 Effective Dart.
- 앱을 만들고 싶다면 flutter.dev의 Flutter 문서.
- 다트와 Flutter의 패키지 저장소인 pub.dev.
자주 묻는 질문
다트는 Flutter 전용 언어인가요?
아니요. 다트는 범용 언어이며 명령줄 도구, 서버, 웹 앱도 만들 수 있습니다. Flutter가 다트를 배우는 가장 인기 있는 이유일 뿐입니다.
다트는 컴파일 언어인가요, 인터프리터 언어인가요?
두 방식이 모두 있습니다. 개발 중에는 JIT 컴파일과 핫 리로드가 되는 VM에서 실행하고, 릴리스할 때는 네이티브 기계어로 AOT 컴파일하거나 웹용 JavaScript 또는 WebAssembly로 컴파일합니다.
다트에서 final과 const의 차이는 무엇인가요?
final 변수는 한 번만 할당되며 실행 시점에 정해져도 됩니다. const 값은 컴파일 시점에 알려져 있어야 하고 깊은 수준까지 불변이며, 덕분에 Flutter가 상수 위젯을 재사용할 수 있습니다.
다트는 자바나 JavaScript와 비슷한가요?
C 스타일 문법과 클래스는 Java, C#, JavaScript 개발자에게 친숙합니다. JavaScript와 달리 다트는 정적 타입 언어이며 건전한 null 안전성을 갖추고 있습니다.
다트의 ?? 연산자는 무엇을 하나요?
a ?? b는 a가 null이 아니면 a를, 아니면 b를 반환합니다. 관련 연산자인 a ??= b는 a가 null일 때만 b를 a에 할당합니다.