Kotlin Nullable Types and Null Safety
Kotlin 기초: Nullable 타입과 Null 안전성
📦 Null이란 무엇인가?
Null이란 ‘값이 없음’ 또는 ‘값이 존재하지 않음’을 나타내는 특별한 값입니다. 변수라는 상자는 만들어졌지만, 아직 그 안에 아무것도 담기지 않은 상태를 의미합니다.
프로그래밍에서 null은 의도적으로 값이 비어있는 상태를 표현할 때 유용하지만, 동시에 NullPointerException과 같은 치명적인 오류의 원인이 되기도 합니다. 따라서 null이 될 수 있는 변수는 항상 신중하게 다루어야 합니다.
📌 Null이 발생할 수 있는 사례
- 사용자 입력 값: 사용자가 폼에 아무것도 입력하지 않고 제출했을 경우.
- 파일 읽기: 존재하지 않는 파일을 읽으려고 시도할 때.
- 데이터베이스 조회: 데이터베이스에서 특정 조건에 맞는 데이터를 찾지 못했을 경우.
⚙️ Nullable 타입 (Nullable Type)
Nullable 타입이란
null값을 가질 수 있는 변수 타입을 의미합니다.
Kotlin에서는 타입 이름 뒤에 물음표(?)를 붙여 해당 변수가 null이 될 수 있음을 명시적으로 선언합니다.
1
2
3
4
5
6
// 이 변수는 String 값 또는 null을 가질 수 있습니다.
val nullableValue: String? = null
// 이 변수는 오직 Int 값만 가질 수 있으며, null을 할당할 수 없습니다.
val nonNullableValue: Int = 10
// nonNullableValue = null // 컴파일 오류 발생!
❓ Nullable 타입이 필요한 이유
컴퓨터는 각 데이터 타입이 지원하는 고유한 기능(함수, 속성)을 알고 있습니다. 예를 들어, String 타입은 길이를 확인하는 .length 속성을 가지고 있습니다. 하지만 null은 아무런 속성이나 기능이 없는 ‘빈 값’이므로 .length와 같은 연산을 수행할 수 없습니다.
Kotlin은 이처럼 null이 될 수 있는 변수와 그렇지 않은 변수를 타입 시스템에서 명확히 구분하여, null에 대해 안전하지 않은 연산을 시도하는 것을 컴파일 시점에 막아줍니다.
🛡️ Kotlin의 Null 안전성 (Null Safety)
Null 안전성(Null Safety)은
NullPointerException(NPE) 오류를 코드에서 최대한 배제하기 위해 설계된 Kotlin의 핵심적인 언어 특징입니다.
🆚 Java와의 차이점
| 구분 | Kotlin | Java |
|---|---|---|
| 기본 | 모든 변수는 기본적으로 null을 허용하지 않습니다 (Non-nullable). | 모든 참조 타입 변수는 기본적으로 null이 될 수 있습니다 (Nullable). |
| 처리 | null 가능성을 타입(?)으로 명시하고, 컴파일러가 null 처리를 강제하여 컴파일 시점에 오류를 잡습니다. | 개발자가 직접 if (variable != null)과 같은 코드로 실행 시점에 null 여부를 확인해야 합니다. |
| 결과 | NullPointerException 발생 가능성이 현저히 낮아져 프로그램의 안정성이 크게 향상됩니다. | 개발자의 부주의로 인해 NullPointerException이 발생할 위험이 항상 존재합니다. |
✍️ Nullable 타입 안전하게 다루기
Kotlin은 null이 될 수 있는 변수를 안전하게 사용하기 위한 여러 가지 편리한 연산자를 제공합니다.
- 조건문으로 직접 확인 (if 문) 가장 기본적인 방법으로,
if를 사용해null이 아님을 직접 확인합니다. 이 블록 안에서 변수는null이 아님이 보장됩니다.1 2 3 4
val name: String? = "Kotlin" if (name != null) { println("이름의 길이는 ${name.length}입니다.") // if 블록 안에서는 스마트 캐스트(Smart Cast)됨 }
- 세이프 콜 연산자 (
?.)?.연산자는 변수가null이 아닐 경우에만 뒤따르는 함수나 속성을 호출하고,null이라면 전체 표현식을null로 반환합니다.1 2
val name: String? = null println(name?.length) // null 출력 (오류 발생하지 않음)
- 엘비스 연산자 (
?:) 변수가null일 때 사용할 기본값을 지정할 수 있습니다.1 2 3
val name: String? = null val displayName = name ?: "Guest" // name이 null이면 "Guest"를 사용 println(displayName) // Guest 출력
- Non-null 단언 연산자 (
!!) 개발자가 해당 변수는 절대로null이 아님을 100% 확신할 때 사용합니다.null임에도 이 연산자를 사용하면NullPointerException이 발생합니다.⚠️ 주의:
!!연산자는null가능성을 컴파일러에게 숨기는 것이므로, 예기치 못한 오류를 발생시킬 수 있습니다. 꼭 필요한 경우가 아니라면 사용을 지양하고 세이프 콜이나 엘비스 연산자를 사용하는 것이 좋습니다.1 2
val name: String? = "Kotlin" println(name!!.length) // 5 출력. 만약 name이 null이었다면 NPE 발생