2. Null-safety

널 처리와 Null-safety

코틀린은 **“널이 될 수 있는 타입은 완전히 다른 타입”**으로 취급합니다. ?를 붙여야만 null 가능, 그에 맞는 도구(?., ?:, !!)를 제공합니다. 자바와 달리 컴파일 단계에서 null 안전성 확보가 가능하다는 점이 핵심입니다.


1) Null은 왜 문제인가?

  • Java의 대표적인 버그 원인: NPE (NullPointerException)

  • Java에서는 모든 참조형 변수에 기본적으로 null이 들어갈 수 있습니다.

  • 방어 코드: if (obj != null)을 반복적으로 작성 → 가독성/생산성 저하

코틀린은 이 문제를 “언어 차원”에서 풀어냅니다.


2) Nullable vs Non-nullable 타입

val a: String = "kotlin"
// a = null   // 컴파일 에러

val b: String? = null   // ? 붙여야 null 가능
  • String → null 불가능

  • String? → null 허용

👉 자바에서 String 하나만 있던 것이 코틀린에선 두 개로 분리됨. 👉 덕분에 컴파일러가 null 가능성을 추적할 수 있습니다.


3) Safe Call (?.)

“null일 때는 그냥 건너뛰자”라는 의도를 코드로 표현합니다.

val len: Int? = b?.length
  • b가 null → 결과는 null

  • b가 non-null → length 실행


4) Elvis 연산자 (?:)

null일 때 기본값을 지정할 수 있습니다.

val len = b?.length ?: 0
  • b가 null → len = 0

  • b가 non-null → 문자열 길이

→ Java의 ternary 연산자 패턴 (b != null ? b.length() : 0)을 훨씬 간결하게 표현.


5) Non-null 단언 (!!)

val len = b!!.length
  • “개발자가 확신하니 그냥 non-null로 취급해!”

  • 만약 null이라면 → NPE 발생

👉 지양할 것. “자바 스타일” NPE를 그대로 불러옵니다. 정말 확신이 있을 때만 사용하세요.


6) 스마트 캐스트 (Smart cast)

코틀린은 null 체크 후 자동으로 타입을 좁혀줍니다.

if (b != null) {
    println(b.length) // 여기선 String으로 캐스트됨
}

자바였다면 if (b != null) { System.out.println(b.length()); }와 같지만, 코틀린은 별도의 캐스팅 없이 안전하게 접근 가능합니다.


7) 플랫폼 타입 (Platform type)

자바와 연동할 때 등장하는 개념입니다 .

// Java
String getName(); // @Nullable 붙어있지 않음
// Kotlin
val name = obj.name  // 타입은 String!
  • 자바에서 null 가능성이 명시되지 않으면, 코틀린은 “플랫폼 타입”(String!)으로 인식

  • 이 경우 컴파일러가 null 여부를 알 수 없어, 개발자가 책임지고 처리해야 함

  • 해결책: 자바 코드에 @Nullable / @NotNull 붙이기, 또는 코틀린 쪽에서 ?/!!로 방어


8) 실무 예시

A. API 응답 DTO

data class UserDto(
    val id: Long,
    val name: String,
    val nickname: String?,  // 옵션 필드
    val email: String?      // 서버에서 null 가능
)

nullable을 타입에 드러내므로 API 클라이언트가 강제적으로 null 처리.

B. Repository 패턴

fun findUser(id: Long): User? {
    return db.findById(id) // 없으면 null
}

val user = repo.findUser(1) ?: throw NotFoundException()

→ Elvis 연산자로 “없으면 바로 예외”를 던지는 패턴.


9) 체크리스트 & 관용구

  • 기본은 non-null 타입

  • null 가능성 있으면 반드시 ? 표기

  • null 접근: ?. (safe call)

  • 기본값 제공: ?: (elvis)

  • 정말 확신 있을 때만 !!

  • 자바 interop → “플랫폼 타입 주의”

  • DTO는 null 가능성을 타입에서 바로 드러내라

  • Android 생명주기/DI 초기화 등은 lateinit 또는 safe call로 처리


10) 미니 치트시트

  • 선언: val s: String? = null

  • safe call: s?.length

  • elvis: s?.length ?: 0

  • not-null 단언: s!!.length (주의)

  • null 체크 후 스마트 캐스트:

    if (s != null) println(s.length)
  • 자바 interop: 어노테이션으로 null 정보 명확히

Last updated