Menu
Coddy logo textTech

? 및 ! 연산자

Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 25번째.

nullable 타입으로 작업할 때, Dart는 값에 안전하게 접근하기 위한 두 가지 필수 연산자를 제공합니다. null-aware access 연산자 ?.null assertion 연산자 !는 잠재적인 null 값을 처리하는 방법을 제어할 수 있게 해줍니다.

?. 연산자는 null이 될 수 있는(nullable) 값의 속성이나 메서드에 안전하게 접근합니다. 값이 null인 경우, 오류가 발생하여 중단되는 대신 전체 표현식은 null을 반환합니다:

String? name = 'Alice';
print(name?.length);  // 5

name = null;
print(name?.length);  // null (오류 발생 안 함!)

! 연산자는 Dart에게 "이 값은 null이 아님을 확신합니다"라고 알려줍니다. 이 연산자는 null 허용(nullable) 타입을 null 비허용(non-nullable) 타입으로 변환하지만, 실제로 값이 null인 경우 에러를 발생시킵니다:

String? maybeName = 'Bob';
String definite = maybeName!;  // 확인 - null이 아님을 단언합니다
print(definite.length);  // 3

String? empty = null;
// String crash = empty!;  // 런타임 에러! 값이 null이었습니다

null 값을 유연하게 처리하고 싶을 때 ?.을 사용하세요. 값이 존재한다는 것이 확실할 때만 !를 사용하세요. 예를 들어 if 문으로 확인한 후이거나 로직상 값이 보장된다는 것을 알 때입니다. !를 남용하면 null 안정성의 목적이 퇴색되고 런타임 충돌이 발생할 수 있습니다.

challenge icon

챌린지

쉬움

?.! 연산자를 사용하여 null 허용 값을 안전하게 다루는 방법을 보여주는 제품 인벤토리 시스템을 구축해 보겠습니다.

코드를 정리하기 위해 두 개의 파일을 생성합니다:

  • product.dart: 인벤토리의 항목을 나타내는 Product 클래스를 정의합니다. 일부 제품은 완전한 정보를 가지고 있지만, 다른 제품은 아직 카탈로그에 등록 중입니다. 클래스에는 다음이 포함되어야 합니다:
    • null 허용되지 않는 String name - 모든 제품은 이름을 가져야 합니다.
    • null 허용되지 않는 double price - 모든 제품은 가격을 가져야 합니다.
    • null 허용되는 String? description - 일부 제품은 아직 설명이 없습니다.
    • null 허용되는 String? category - 일부 제품은 아직 카테고리가 지정되지 않았습니다.
    • nameprice를 필수 매개변수로 받는 생성자
    • ?.를 사용하여 설명의 길이를 반환하거나, 설명이 없으면 null을 반환하는 getDescriptionLength() 메서드
    • !를 사용하여 카테고리를 대문자로 반환하는 getCategoryUppercase() 메서드 - 이 메서드는 카테고리가 이미 설정되었다고 가정합니다.
    • 제품 정보를 출력하는 displayProduct() 메서드. 설명이 null일 때는 "No description"을, 카테고리가 null일 때는 "Uncategorized"를 표시합니다.
  • main.dart: 제품 클래스를 임포트하고 두 연산자를 모두 시연합니다:
    • 이름이 'Laptop'이고 가격이 999.99인 제품을 생성한 다음, 설명을 'High-performance computing device'로, 카테고리를 'Electronics'로 설정합니다.
    • 이름이 'Mystery Item'이고 가격이 49.99인 제품을 생성합니다 (설명과 카테고리는 null로 둡니다).
    • Laptop의 경우: displayProduct()를 호출하고, getDescriptionLength()를 사용하여 Description length: [length]를 출력하며, getCategoryUppercase()를 사용하여 Category: [uppercase]를 출력합니다.
    • Mystery Item의 경우: displayProduct()를 호출하고, getDescriptionLength()를 사용하여 Description length: [length]를 출력합니다 (이때 null이 표시됩니다).

displayProduct() 메서드는 다음 형식으로 출력해야 합니다:

Product: [name] - $[price]
Description: [description or "No description"]
Category: [category or "Uncategorized"]

예상 출력 결과:

Product: Laptop - $999.99
Description: High-performance computing device
Category: Electronics
Description length: 32
Category: ELECTRONICS
Product: Mystery Item - $49.99
Description: No description
Category: Uncategorized
Description length: null

치트 시트

null-aware access 연산자 ?.는 nullable 값의 속성이나 메서드에 안전하게 접근합니다. 값이 null인 경우, 에러를 발생시키는 대신 null을 반환합니다:

String? name = 'Alice';
print(name?.length);  // 5

name = null;
print(name?.length);  // null (충돌 없음!)

null 단언 연산자 !는 nullable 타입을 non-nullable 타입으로 변환합니다. 값이 null이 아님이 확실할 때만 사용하세요. 실제 값이 null인 경우 런타임 에러가 발생하기 때문입니다:

String? maybeName = 'Bob';
String definite = maybeName!;  // OK - null이 아님을 단언함
print(definite.length);  // 3

String? empty = null;
// String crash = empty!;  // 런타임 에러!

유연한 null 처리를 위해서는 ?.를 사용하고, 값이 존재한다는 것이 절대적으로 확실할 때만 !를 사용하세요.

직접 해보기

import 'product.dart';

void main() {
  // TODO: 가격이 999.99인 'Laptop'이라는 이름의 제품을 생성하세요
  // 그런 다음 설명을 'High-performance computing device'로 설정하세요
  // 그리고 카테고리를 'Electronics'로 설정하세요
  
  // TODO: 가격이 49.99인 'Mystery Item'이라는 이름의 제품을 생성하세요
  // 설명과 카테고리는 null로 남겨두세요
  
  // TODO: Laptop 제품에 대해 다음을 수행하세요:
  // - displayProduct()를 호출하세요
  // - getDescriptionLength()를 사용하여 "Description length: [length]"를 출력하세요
  // - getCategoryUppercase()를 사용하여 "Category: [uppercase]"를 출력하세요
  
  // TODO: Mystery Item 제품에 대해 다음을 수행하세요:
  // - displayProduct()를 호출하세요
  // - getDescriptionLength()를 사용하여 "Description length: [length]"를 출력하세요
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨