About

Dart Basic Syntax: var/final/const and the ?, !, ?? Operators

Core Dart syntax, starting from main()

Dart programs start from main(). The core syntax includes 6 variable declaration styles (var, final, const, late, dynamic), string interpolation, Null Safety operators (?., ??, !, ??=), and the cascade (..) operator. Mastering these basics is essential for reading and writing Flutter code naturally.

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이면 전체 표현식이 nullname?.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 참조

Implementation Steps

1

var vs final vs const — var is reassignable, final is runtime constant, const is compile-time constant

2

late keyword — declare without initialization, essential for lazy init of non-nullable variables

3

String interpolation — insert values with "$var" or "${expr}", preferred over + concatenation

4

Null Safety operators — ?. (null returns null), ?? (default if null), ! (non-null assert, risky), ??= (assign if null)

5

Cascade (..) operator — chain calls on same object, useful like builder pattern

Pros

  • ✓ Clear immutability expression with final/const
  • ✓ Null Safety prevents NullPointerException at compile time

Cons

  • ✗ final vs const distinction can confuse beginners

Use Cases

Understanding variables and Null Safety before writing Flutter code
← What Is Dart? A Language Built on Null Safety and JIT/AOT How Dart's Type System Works: Inference, Casts, and Generics →