본문으로 바로가기
TaeyoungKim.dev

Dart String?와 ! 차이: null 값에 !를 붙이면 왜 런타임 오류가 날까?

앱작성 약 3분 읽기TaeyoungKim
LinkedInX

화면 제목이 아직 들어오지 않았는데 길이를 읽으려고 title!.length를 썼더니 앱이 멈춘다. !가 null을 없애 주는 것처럼 보일 수 있지만 실제로는 “여기는 null이 아니다”라고 개발자가 단언하는 표시다. 단언이 틀리면 실행 중 오류가 난다. String?에 담긴 값이 없을 수 있다는 사실은 그대로 남아 있다.

String?와 !는 각각 무엇을 뜻할까?

도식의 성공 경로는 실제 문자열이 있을 때뿐이다. !는 null을 없애거나 검사하는 연산이 아니므로, 값이 없는 경로에서는 같은 표현이 런타임 오류로 갈라진다.

String은 null을 허용하지 않고 String?는 문자열 또는 null을 담을 수 있다. !는 타입 이름 뒤에 쓰는 새 타입이 아니라 nullable 표현식 뒤에 붙이는 연산자다. 예를 들어 title!은 그 순간의 title이 null이 아니라고 강제한다. null이라면 값이 만들어지지 않고 오류가 발생한다.

dart
String? readLabel(bool found) => found ? '안내' : null;
int requiredLength(String? input) => input!.length;

void main() {
  final String? label = readLabel(false);
  print(label?.length ?? 0);

  try {
    print(requiredLength(label));
  } catch (error) {
    print(error.runtimeType);
  }
}

첫 출력은 0이다. ?.는 값이 있을 때만 length를 읽고, null이면 ?? 0의 기본값을 쓴다. 두 번째 경로는 input!에서 null을 단언해 런타임 오류가 나므로 예제에서는 오류 타입을 출력한다. 실제 화면 코드에서는 이런 오류를 일부러 낼 필요가 없다. 예제는 두 연산자의 결과를 비교하기 위한 것이다.

null일 때 무엇을 해야 하는지 먼저 결정한다

값이 없을 때 0이나 대체 문구가 합당하다면 ??로 기본값을 준다. 값이 없으면 화면을 그리지 않거나 입력을 다시 요청해야 한다면 if (label == null)로 분기하고 그 뒤에만 값을 사용한다. !는 외부에서 값을 보장한다는 실제 불변 조건이 확인될 때만 제한적으로 쓴다. 단지 컴파일러 경고를 지우기 위해 붙이면 오류를 뒤로 미루는 셈이다.

dart
String labelText(String? label) {
  if (label == null) return '제목 없음';
  return label;
}

이 함수는 null일 때의 동작을 코드에 드러낸다. 사용자가 아직 제목을 입력하지 않은 상태가 정상이라면 이런 분기가 !보다 읽기 쉽다. 반대로 제목이 필수인 데이터 모델이라면 입력 경계에서 유효성을 검사하고, 앱 안쪽에는 null을 허용하지 않는 타입을 전달하는 설계를 검토할 수 있다.

오류가 나면 어디서 확인할까?

오류가 난 줄의 ! 앞 표현식이 무엇인지 확인하고, 그 값이 들어오는 시점과 빈 값이 가능한 경로를 추적한다. 비동기 로딩 전, 선택 해제, 데이터 누락처럼 null이 자연스럽게 생기는 순간이 있는지 테스트한다. 정상 데이터 한 건만으로는 이 문제를 발견하기 어렵다. 값이 있을 때와 없을 때를 모두 실행하고, 예상한 대체 문구나 분기가 동작하는지 본다.

이 연산자의 정확한 의미는 Dart 언어의 연산자 설명에서도 확인할 수 있다. 운영 오류를 임시로 잡기 위해 !를 여러 곳에 퍼뜨리기보다, 어느 경계에서 필수값으로 만들지 정하는 편이 오류 범위를 줄인다.

핵심 요약: !는 null을 고치는 도구가 아니다

String?는 null 가능성을 표시하고, 값!은 그 값이 null이 아니라고 단언한다. 단언이 틀리면 런타임 오류가 난다. 값 없음이 정상인지 오류인지 먼저 정하고, ?., ??, 명시적 분기 또는 입력 검증 중 맞는 방법을 고르자.

작성자

TaeyoungKim

기초 개념을 구현과 검증, 실제 운영 판단까지 연결해 기록합니다.

#Dart#Null Safety#String?#null check#런타임 오류

함께 읽으면 좋은 글