Kotlin Null Safety
한 줄 정의
Kotlin null safety는 null이 가능한 타입과 불가능한 타입을 타입 시스템에서 구분해 null 처리 누락을 컴파일 타임에 줄이는 기능이다.
실무에서 왜 중요한가
백엔드 서비스에서 null은 요청값, DB 조회 결과, 외부 API 응답, 설정값에서 자주 발생한다. Kotlin을 사용해도 다음 상황은 계속 주의해야 한다.
- Java API가 반환한 platform type에서 NPE가 발생한다.
!!를 남용해서 Kotlin 코드에서도 NPE가 발생한다.- JPA Entity의 지연 초기화 필드와 non-null 타입이 충돌한다.
- Jackson 역직렬화를 위해 nullable/default value 설계가 필요하다.
- null과 빈 문자열, 빈 컬렉션의 의미가 섞인다.
기본 개념
Kotlin은 타입 뒤에 ?가 붙어야 null을 담을 수 있다.
val name: String = "kim"
val nickname: String? = nullnullable 값은 바로 사용할 수 없다.
val length = nickname.length // compile error안전 호출이나 명시적 처리가 필요하다.
val length = nickname?.length ?: 0실무 처리 방식
| 상황 | 권장 |
|---|---|
| 요청 필수값 | non-null 타입 + validation |
| 요청 선택값 | nullable 타입 |
| 조회 결과 없음 | nullable 또는 명시적 예외 |
| 목록 없음 | null보다 empty list 우선 |
| 외부 API 불확실한 값 | nullable DTO로 받고 검증 후 내부 모델 변환 |
외부 입력과 내부 도메인 모델의 nullability를 분리하는 것이 좋다.
data class CreateUserRequest(
val name: String?,
val email: String?
)
data class CreateUserCommand(
val name: String,
val email: String
)요청 DTO에서는 입력 불완전성을 표현하고, 검증 후 내부 command는 non-null로 만드는 식이다.
!! 사용 주의
!!는 null이면 즉시 NPE를 던진다.
val length = nickname!!.length실무에서는 !!가 많아지면 Kotlin null safety의 장점이 사라진다. 정말 불가능한 상태라면 예외 메시지를 명확히 남기는 편이 낫다.
val user = userRepository.findByIdOrNull(id)
?: throw UserNotFoundException(id)자주 나는 실수
!!로 컴파일 오류를 빠르게 없앤다.- Java API 반환값을 non-null이라고 단정한다.
- nullable field를 그대로 여러 계층으로 전파한다.
- 빈 컬렉션이면 충분한데 nullable collection을 사용한다.
- validation 전 request DTO를 domain 객체처럼 사용한다.
확인 방법
- 코드 리뷰에서
!!사용 위치와 이유를 확인한다. - Java API 호출부의 반환값 null 가능성을 확인한다.
- request DTO와 내부 command/domain 모델의 nullability가 분리되어 있는지 확인한다.
- 테스트에서 누락 필드, null 필드, 빈 문자열을 구분해 검증한다.
핵심 요약
Kotlin null safety는 null 가능성을 타입으로 표현해 null 처리 누락을 줄인다.
하지만 Java interop의 platform type, !! 남용, JPA/Jackson 경계에서는 여전히 NPE가 발생할 수 있다.
외부 입력 DTO는 nullable로 받을 수 있지만, 검증 후 내부 모델은 non-null로 바꾸는 것이 안전하다.
빈 목록은 null보다 empty list로 표현하는 편이 호출부를 단순하게 만든다.
질문을 받으면 Kotlin이 NPE를 완전히 제거하는 것이 아니라 컴파일 타임에 null 위험을 줄인다고 설명하는 것이 정확하다.
꼬리 질문
Kotlin의 nullable 타입과 non-null 타입 차이는?
String은 null을 담을 수 없고,String?은 null을 담을 수 있습니다. nullable 값은 안전 호출이나 null 처리를 거쳐야 사용할 수 있습니다.
!!는 왜 위험한가?null이면 런타임에 NPE를 던지므로 Kotlin null safety의 장점을 우회합니다. 명시적 예외나 안전한 변환을 우선 고려해야 합니다.
Kotlin을 써도 NPE가 발생할 수 있는 경우는?
Java interop의 platform type,
!!, 초기화 전 접근, reflection/Jackson/JPA 경계에서 발생할 수 있습니다.
점검 퀴즈
아래 문항은 개념을 실제로 설명할 수 있는지 점검하기 위한 것이다. 선택지를 누르면 정답 여부와 이유가 표시된다.
객관식Kotlin의 nullable 타입과 non-null 타입 차이로 가장 적절한 설명은?
OX!!는 컴파일러에게 null이 아니라고 단언하는 것이므로, 잘못 쓰면 NPE가 발생할 수 있다.
객관식Kotlin 코드에서도 NPE가 발생할 수 있는 대표적인 경우는?