kotlin `!!` 쓰지 마시오

오리·2025년 5월 28일

!! 개념

Kotlin의 !! 문법은 non-null 단언 연산자라고 부르며, 이 표현은 이 값은 절대로 null이 아님을 내가 확신한다고 컴파일러에 말해주는 용도, 만약 그 값이 실제로 null이면, 런타임에 NullPointerException이 발생

!!와 ? 차이점

!! : Not-null assertion operator

  • Nullable 타입을 강제로 Non-null 타입으로 변환
  • 만약 값이 null이면 즉시 NullPointerException(NPE) 발생
val str: String? = null
val len = str!!.length *// NPE 발생*
  • "나는 이 값이 절대 null이 아니라고 확신한다"는 의도를 명시

? : Safe call operator

  • 객체가 null이 아니면 해당 프로퍼티/메서드 접근, null이면 전체 결과가 null
val str: String? = null
val len = str?.length *// 결과: null*
  • null을 안전하게 다루기 위한 Kotlin의 핵심 기능

다른 언어에서는?

  1. Swift (옵셔널 언래핑 강제)
let str: String? = nil

let length = str!.count  // 런타임 크래시 발생
  • Swift의 !는 Kotlin의 !!와 거의 동일한 역할입니다.
  • 옵셔널 값을 강제로 언래핑함.
  1. TypeScript
let str: string | null = null

let length = str!.length; // 컴파일러에 non-null임을 단언
  • !를 사용해 non-null 단언 (Kotlin의 !!보다 컴파일 시점에 작동).
  • 런타임에서 null이면 오류는 발생하지만 명시적 NPE는 아님.
  1. C# (.NET 6 이상부터 nullable support)
string? s = null;
int len = s!.Length; // Non-null 단언 연산자 (!)
  • !는 null-forgiving operator라고 부름.
  • 컴파일러 경고를 무시하고 “null 아님”을 주장.

!!를 쓰면 안 되는 이유

  • Kotlin의 가장 큰 장점인 null safety를 무력화
  • null이 들어올 수 있는 상황에서 !!를 쓰면 런타임에 예기치 않은 NPE로 앱이 즉시 크래시
  • 디버깅이 어렵고, 코드의 안정성을 떨어뜨림
  • "Kotlin을 쓰는 의미가 없다"는 평가까지 있음

!!는 Kotlin의 null safety 기능을 포기하는 것이며, 예외 발생 시 어디서 왜 발생했는지 파하기 어렵다.


스마트 캐스팅(smart casting)과 !!의 관계

  • 스마트 캐스팅
    • Kotlin 컴파일러가 타입 체크 후, 안전하다고 판단되면 자동으로 타입을 변환해줌

      
      fun printLength(obj: Any?) {
          if (obj is String) {
              println(obj.length) *// obj는 String으로 smart cast*
          }
      }
    • 별도의 캐스팅 연산자 없이도 안전하게 타입 변환

  • !!를 써야 하는 경우
    • 스마트 캐스팅이 불가능한 상황(예: 지역 변수 아님, var로 선언, 람다 밖에서 접근 등)에서는 컴파일러가 non-null임을 보장할 수 없어 !!로 강제 변환 필요할 수 있음
    • 하지만, 이런 경우에도 null이 올 수 있으므로 최대한 피해야 함.

let으로 대체하는 것이 좋은 이유

  • let은 scope function으로, safe call(?.)과 함께 쓰면 객체가 null이 아닐 때만 블록 실행
  • NPE 위험 없이 null-safe하게 코드를 작성할 수 있음
val str: String? = ...
str?.let { println(it.length) }
  • 불필요한 NPE 발생 없이, null이 아닌 상황에서만 안전하게 작업 가능
  • 코드 가독성, 안정성, 유지보수성이 모두 향상됨

Q&A

Q: 정말로 null이 올 수 없는 상황이면 !! 써도 되나요?

A: 컴파일러가 보장하지 못하는 상황에서만, 그리고 정말 100% 확신할 때만.

하지만 대개 더 안전한 구조로 리팩터링하는 것이 권장됨

결론 및 Best Practice

예시 코드 비교

*// Bad: NPE 위험*
val name: String? = null
val length = name!!.length *// NPE

// Good: let + safe call*
name?.let { println(it.length) } *// 아무 일도 일어나지 않음, 안전*
  • !!는 정말 불가피한 상황이 아니면 절대 사용하지 않는다.
  • null-safe 처리가 필요하다면 ?.let { ... } 패턴을 적극 활용한다.
  • 스마트 캐스팅이 가능한 구조로 코드를 설계한다.
  • 코드 리뷰에서 !!가 보이면 반드시 이유를 확인한다.
  • !!는 Kotlin의 null safety를 무력화하므로 사용을 최대한 지양한다.
  • !!은 최후의 수단일 뿐, 대체 방법이 항상 있다!

0개의 댓글