Flutter

Flutter Day 3-1: Dart Null Safety

@leem 2025. 1. 6. 15:04

1. 널 세이프티(Null Safety)란?

널 세이프티(Null Safety)는 다트(Dart) 언어에서 널(null)로 인한 오류를 방지하기 위한 기능입니다.
다트 컴파일러는 널이 될 수 있는 타입널이 될 수 없는 타입을 명확히 구분하여,
컴파일 시점에 잘못된 널 참조를 미리 감지해 줍니다.

Sound Null Safety란?

  • 사운드 타입 시스템(Sound Type System):
    컴파일러가 널 안전성 규칙을 엄격히 적용하여, 런타임 중 널 포인터 예외를 방지합니다.
  • 잘못된 널 참조는 프로그램을 중단시키는 주요 원인 중 하나입니다.
    널 세이프티는 이를 방지하여 안정적인 코드를 작성하도록 돕습니다.

2. 기본 사용법

void main() {
  String name = '길동'; // 널 허용하지 않는 String 타입
  int age = 30;

  String? nullableName; // 널 허용 String 타입
  int? nullableAge; // 널 허용 int 타입

  print('name: ${name}');
  // print(nullableName.length); // 컴파일 오류: nullableName이 null일 수 있음
  print('nullableAge: ${nullableAge}');

  // 방어적 코드
  if (nullableName != null) {
    print(nullableName.length); // null이 아닌 경우에만 안전하게 사용
  }
}
  1. String: null 값을 허용하지 않는 타입.
  2. String?: null 값을 허용하는 옵셔널 타입.
  3. 컴파일 시점 오류 감지: nullableName.length는 null 가능성이 있으므로 오류 발생.

3. 널 체크 연산자 (?.)와 널 병합 연산자 (??)

널 체크 연산자 (?.)

객체가 null인지 확인한 후, null이 아니면 속성이나 메서드에 접근합니다.

널 병합 연산자 (??)

값이 null이면 기본값을 반환합니다.

void main() {
  // 널 체크 연산자
  String? userName = getNullableUserName();
  int? userNameLength = userName?.length; // null이면 null 반환
  print('사용자 이름의 길이: $userNameLength');

  // 널 병합 연산자
  String finalUserName = userName ?? '홍길동'; // null이면 기본값 사용
  print('finalUserName: $finalUserName');

  // ?.와 ?? 함께 사용
  String upperOrNoName = userName?.toUpperCase() ?? 'NO_NAME'; // null 체크 후 기본값
  print('upperOrNoName: $upperOrNoName');
}

String? getNullableUserName({String? name}) {
  return name;
}

 

출력 결과

사용자 이름의 길이: null
finalUserName: 홍길동
upperOrNoName: NO_NAME

 


4. 널 억제 연산자 (!)

널 체크 없이 null이 아님을 확신하고, 강제로 값을 사용합니다.

void main() {
  String? nullableName = getNullableName();

  // null 억제 연산자 사용
  String forcedName = nullableName!;
  print('forcedName: $forcedName');
}

String? getNullableName() {
  return '홍길동';
}

 

주의

  • nullableName이 실제로 null이면 런타임 오류가 발생합니다.
    반드시 null이 아님을 확신할 때만 사용하세요.

5. late 키워드

변수를 나중에 초기화할 때 사용합니다.
초기화하지 않고 사용하려 하면 컴파일러가 오류를 감지합니다.

void main() {
  // late 변수 선언
  late String greeting;
  greeting = getGreetingMessage();
  print(greeting);

  // late final 변수
  late final int number;
  number = 100; // 초기화
  print(number);

  // 초기화하지 않은 late 변수 사용 시 오류
  late String notAssigned;
  // print(notAssigned); // 오류 발생
}

String getGreetingMessage() {
  print('함수 호출됨');
  return 'Hello, Dart!';
}

 

출력 결과

함수 호출됨
Hello, Dart!
100

 

  • late: 변수를 지연 초기화. 필요할 때 값을 설정.
  • late final: 초기화 후 변경 불가능.
  • 초기화하지 않고 사용하면 컴파일 오류 발생.

6. Dart Null Safety의 핵심 요약

기능 설명 예제
? (null 가능 타입) null 값을 허용하는 타입. String? nullableName;
?. null이면 null 반환. null이 아니면 속성/메서드 접근. nullableName?.length
?? null이면 기본값 반환. nullableName ?? '기본값'
! (null 억제) null 아님을 확신할 때 사용. null이면 런타임 오류 발생. nullableName!
late 나중에 초기화되는 변수. 초기화하지 않고 사용 시 컴파일 오류. late String greeting; greeting = 'Hi';