Dart基本文法
変数宣言、文字列、演算子、Null Safety基礎
Dartプログラムはmain()から始まります。var、final、const、late、dynamicの6種変数宣言、文字列補間、Null Safety演算子(?., ??, !, ??=)、カスケード(..)演算子がコア文法です。これらの基礎を固めてこそFlutterコードを自然に読み書きできます。
Dart 프로그램의 구조
모든 Dart 프로그램은 main() 함수에서 시작합니다. void는 반환값이 없음을 의미합니다.
void main() {
print('안녕하세요, Dart!');
}
// 커맨드라인 인자를 받는 경우
void main(List<String> arguments) {
print('프로그램 인자: $arguments');
}주석 (Comments)
// 한 줄 주석
/*
여러 줄 주석
여러 줄에 걸쳐 작성할 수 있습니다.
*/
/// 문서화 주석
/// dartdoc 도구가 API 문서를 생성할 때 사용합니다.
/// 클래스, 함수, 변수 등의 설명을 작성할 때 유용합니다.기본 데이터 타입
// 숫자 타입
int integerValue = 42; // 정수
double doubleValue = 3.14; // 실수
num numValue = 10; // int나 double의 상위 타입
// 문자열 타입
String greeting = '안녕하세요';
// 불리언 타입
bool isTrue = true;
bool isFalse = false;
// 리스트 (배열)
List<int> numbers = [1, 2, 3, 4, 5];
// 맵 (key-value 쌍)
Map<String, dynamic> person = {
'name': '홍길동',
'age': 30,
'isStudent': false
};
// 집합 (중복 없는 컬렉션)
Set<String> uniqueNames = {'홍길동', '김철수', '이영희'};변수 선언 방식 비교
| 키워드 | 타입 추론 | 재할당 | 초기화 시점 |
|---|---|---|---|
| var | 자동 | 가능 | 선언 시 |
| final | 자동 | 불가 | 런타임 |
| const | 자동 | 불가 | 컴파일 타임 |
| late | 수동 | 가능 | 나중에 |
| dynamic | 없음 | 가능 (타입 변경도 가능) | 선언 시 |
var — 타입 추론 변수
초기값에서 타입이 자동 추론되며, 이후 다른 타입의 값은 할당할 수 없습니다.
var name = '홍길동'; // String으로 추론
var age = 30; // int로 추론
var height = 175.5; // double으로 추론
name = '김철수'; // 가능 (String → String)
// name = 42; // 오류 (String → int)명시적 타입 선언
String name = '홍길동';
int age = 30;
double height = 175.5;final과 const
// final: 런타임에 값이 결정되는 상수
final String name = '홍길동';
final currentTime = DateTime.now(); // 타입 추론 가능
// const: 컴파일 타임에 값이 결정되는 상수
const int maxUsers = 100;
const double pi = 3.14159;
// 재할당 불가
// name = '김철수'; // 오류: final 변수는 재할당 불가
// maxUsers = 200; // 오류: const 변수는 재할당 불가final vs const 핵심 차이
final은 런타임에 값이 결정될 수 있지만, const는 컴파일 시점에 값을 알 수 있어야 합니다.
final now = DateTime.now(); // 가능 (런타임 값)
// const today = DateTime.now(); // 오류: 컴파일 시점에 값을 알 수 없음late — 지연 초기화
선언 시 초기화하지 않고 나중에 값을 할당합니다. 초기화 전에 접근하면 런타임 오류가 발생합니다.
late String name;
void initName() {
name = '홍길동'; // 나중에 값 할당
}
void main() {
initName();
print(name); // '홍길동'
late String address;
// print(address); // 오류: 초기화되지 않은 late 변수에 접근
}dynamic — 동적 타입
어떤 타입의 값이든 할당 가능하며, 컴파일 타임 타입 검사를 우회합니다.
dynamic value = '문자열';
print(value); // '문자열'
value = 42;
print(value); // 42
value = true;
print(value); // true문자열 (String)
작은따옴표와 큰따옴표 모두 사용할 수 있습니다.
String single = '작은따옴표 문자열';
String double_ = "큰따옴표 문자열";문자열 보간 (Interpolation)
String name = '홍길동';
int age = 30;
// $변수명 형태
String message = '제 이름은 $name이고, 나이는 $age살입니다.';
// ${표현식} 형태
String ageNextYear = '내년에는 ${age + 1}살이 됩니다.';여러 줄 문자열 & 원시 문자열
// 여러 줄 문자열 — 삼중 따옴표
String multiLine = '''
이것은
여러 줄에 걸친
문자열입니다.
''';
// 원시 문자열 — r 접두사 (이스케이프 처리 안 함)
String escaped = 'C:\Program Files\Dart'; // 이스케이프 사용
String raw = r'C:\Program Files\Dart'; // 원시 문자열산술 연산자
int a = 10;
int b = 3;
print(a + b); // 13 (덧셈)
print(a - b); // 7 (뺄셈)
print(a * b); // 30 (곱셈)
print(a / b); // 3.333... (나눗셈, 결과는 double)
print(a ~/ b); // 3 (정수 나눗셈, 결과는 int)
print(a % b); // 1 (나머지)/는 항상 double을 반환합니다. 정수 결과가 필요하면 ~/를 사용하세요.
증감 & 할당 연산자
int a = 10;
a++; // 후위 증가 → 11
++a; // 전위 증가 → 12
a--; // 후위 감소 → 11
--a; // 전위 감소 → 10
// 할당 연산자
a += 5; // a = a + 5 → 15
a -= 3; // a = a - 3 → 12
a *= 2; // a = a * 2 → 24
a ~/= 5; // a = a ~/ 5 → 4비교 & 논리 연산자
// 비교 연산자
int a = 10, b = 5;
print(a == b); // false (같음)
print(a != b); // true (다름)
print(a > b); // true (초과)
print(a < b); // false (미만)
print(a >= b); // true (이상)
print(a <= b); // false (이하)
// 논리 연산자
bool c1 = true, c2 = false;
print(c1 && c2); // false (AND)
print(c1 || c2); // true (OR)
print(!c1); // false (NOT)타입 테스트 연산자
var value = '문자열';
print(value is String); // true (String 타입인지 확인)
print(value is! int); // true (int 타입이 아닌지 확인)
// as 연산자 — 명시적 타입 변환
dynamic someValue = 'Dart';
String text = someValue as String;조건 연산자
// 삼항 연산자: 조건 ? 값1 : 값2
int a = 10, b = 5;
int max = a > b ? a : b;
print(max); // 10
// ?? 연산자: 왼쪽이 null이면 오른쪽 반환
String? name;
String displayName = name ?? '이름 없음';
print(displayName); // '이름 없음'캐스케이드(..) 연산자
동일 객체에 연속으로 메서드를 호출하거나 속성을 설정할 때 사용합니다.
class Person {
String name = '';
int age = 0;
void introduce() {
print('내 이름은 $name이고, 나이는 $age살입니다.');
}
}
void main() {
var person = Person()
..name = '홍길동'
..age = 30
..introduce();
// 위 코드는 다음과 동일합니다:
// var person = Person();
// person.name = '홍길동';
// person.age = 30;
// person.introduce();
}Null Safety
Dart 2.12부터 도입된 Null Safety는 컴파일 타임에 null 참조 오류를 방지합니다.
// non-nullable 타입 (null 할당 불가)
String name = '홍길동';
// name = null; // 컴파일 오류
// nullable 타입 (? 추가 → null 허용)
String? nullableName = '홍길동';
nullableName = null; // 허용됨Null Safety 연산자 비교
| 연산자 | 이름 | 동작 | 예시 |
|---|---|---|---|
| ?. | 조건부 접근 | 객체가 null이면 전체 표현식이 null | name?.length |
| ?? | null 병합 | 왼쪽이 null이면 오른쪽 반환 | name ?? '기본값' |
| ??= | null 할당 | 변수가 null이면 값을 할당 | name ??= '기본값' |
| ! | non-null 단언 | null이 아님을 단언 (위험) | name! |
Null Safety 연산자 코드 예제
String? name = getNullableName();
// null 검사 후 사용
if (name != null) {
print('이름의 길이: ${name.length}');
}
// ?. — 조건부 접근 (null이면 null 반환)
print('이름의 길이: ${name?.length}');
// ?? — null 병합 (null이면 기본값)
print('이름: ${name ?? '이름 없음'}');
// ??= — null이면 값을 할당
name ??= '이름 없음';non-null 단언 연산자 (!)
주의: ! 연산자는 실제 null인 경우 런타임 오류를 발생시킵니다. 가능하면 ??나 ?.을 우선 사용하세요.
String? name = '홍길동';
// null이 아니라고 확신할 때 사용
String nonNullName = name!;
// 실제로 null이면 런타임 오류 발생
name = null;
// String error = name!; // 런타임 오류: null 참조実装ステップ
var vs final vs const — varは再代入可、finalはランタイム定数、constはコンパイル定数
lateキーワード — 宣言時に初期化せず後で代入、Null Safetyでのnon-nullable変数の遅延初期化に必須
文字列補間 — "$変数"または"${式}"で文字列内に値挿入、+連結より推奨
Null Safety演算子 — ?.(nullならnull返却)、??(nullならデフォルト値)、!(non-null断言、危険)、??=(nullなら代入)
カスケード(..)演算子 — 同一オブジェクトへの連続呼び出し、ビルダーパターン的に活用可能
メリット
- ✓ final/constで不変性を明確に表現可能
- ✓ Null SafetyでNullPointerExceptionをコンパイル時に防止
デメリット
- ✗ final vs constの区別が初心者に混乱を招く可能性