[2026] Kotlin 변수와 타입 | val, var, 기본 타입 완벽 정리
이 글의 핵심
var score = 90 score = 95.
들어가며
val·var와 널 안전성(?, !!, ?.)으로 불변을 기본에 두고, 널 가능성을 타입에 적습니다. 컴파일러가 흐름 분석으로 불필요한 null 검사를 줄여 줍니다.
실무에서 마주한 현실
개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.
1. 변수 선언
val (불변)
val name = "홍길동"
val age = 25
// name = "김철수" // 컴파일 에러!
var (가변)
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
var score = 90
score = 95 // OK
var count = 0
count++ // OK
선택 기준
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 기본적으로 val 사용 (권장)
val pi = 3.14
val maxSize = 100
// 변경이 필요한 경우만 var
var currentPage = 1
var isLoggedIn = false
2. 기본 타입
숫자 타입
다음은 kotlin를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 정수
val byte: Byte = 127
val short: Short = 32767
val int: Int = 2147483647
val long: Long = 9223372036854775807L
// 실수
val float: Float = 3.14f
val double: Double = 3.14159
// 타입 추론
val number = 10 // Int
val bigNumber = 10L // Long
val decimal = 3.14 // Double
val precise = 3.14f // Float
문자와 문자열
다음은 kotlin를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 문자
val char: Char = 'A'
val koreanChar = '가'
// 문자열
val text: String = "Hello"
val multiLine = """
여러 줄
문자열
""".trimIndent()
// 문자열 템플릿
val name = "홍길동"
val greeting = "안녕하세요, $name님!"
val info = "나이: ${age + 1}세"
불리언
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
val isActive: Boolean = true
val isCompleted = false
// 논리 연산
val result = isActive && !isCompleted
3. Nullable 타입
Non-null vs Nullable
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Non-null (기본)
var name: String = "홍길동"
// name = null // 컴파일 에러!
// Nullable
var nullableName: String? = "홍길동"
nullableName = null // OK
Safe Call (?.)
Nullable 타입을 안전하게 다루는 핵심 연산자입니다: 아래 코드는 kotlin를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
val name: String? = null
// Safe Call: null이면 null 반환, 아니면 프로퍼티/메서드 호출
val length = name?.length
// name이 null이면 length도 null
// name이 "홍길동"이면 length는 3
// Java의 if (name != null) name.length() 를 간결하게 표현
// 체이닝 가능 - 중간에 null이 있으면 전체가 null
val city = user?.address?.city
// user가 null이면 → null
// user.address가 null이면 → null
// 둘 다 있으면 → user.address.city
// Java라면 여러 줄의 null 체크가 필요
Elvis 연산자 (?:)
null일 때 기본값을 제공하는 연산자입니다: 다음은 kotlin를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
val name: String? = null
// Elvis 연산자: null이면 오른쪽 값 사용
val length = name?.length ?: 0
// name?.length가 null이면 0 반환
// name?.length가 3이면 3 반환
// 기본값 제공
val displayName = name ?: "Guest"
// name이 null이면 "Guest"
// name이 "홍길동"이면 "홍길동"
// 실전 예시: 사용자 이름 표시
fun greet(name: String?) {
val greeting = "안녕하세요, ${name ?: "방문자"}님!"
println(greeting)
}
greet(null) // 안녕하세요, 방문자님!
greet("홍길동") // 안녕하세요, 홍길동님!
Not-null 단언 (!!)
“이 값은 절대 null이 아니다”라고 컴파일러에게 단언합니다: 다음은 kotlin를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
val name: String? = "홍길동"
// !! : null이 아님을 단언
val length = name!!.length
// name이 null이면 KotlinNullPointerException 발생
// name이 "홍길동"이면 3 반환
// 주의: 확실할 때만 사용!
// 가능하면 ?. 또는 ?: 사용 권장
// 나쁜 예
val user: User? = getUser()
val name = user!!.name // null이면 크래시!
// 좋은 예
val name = user?.name ?: "Unknown" // null 안전
연산자 비교:
| 연산자 | 동작 | null일 때 |
|---|---|---|
?. | Safe Call | null 반환 |
?: | Elvis | 기본값 반환 |
!! | Not-null 단언 | 예외 발생 |
Safe Cast (as?)
val obj: Any = "Hello"
val str: String? = obj as? String // 성공
val num: Int? = obj as? Int // null 반환
4. 타입 변환
명시적 변환
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
val int: Int = 10
val long: Long = int.toLong()
val double: Double = int.toDouble()
val string: String = int.toString()
// 모든 변환 메서드
val byte = int.toByte()
val short = int.toShort()
val float = int.toFloat()
문자열 변환
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 문자열 → 숫자
val str = "123"
val num = str.toInt()
val decimal = str.toDouble()
// 안전한 변환
val num = str.toIntOrNull() ?: 0
5. 타입 체크와 스마트 캐스트
is 연산자
아래 코드는 kotlin를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
val obj: Any = "Hello"
if (obj is String) {
// 자동으로 String으로 캐스트
println(obj.length)
}
// not is
if (obj !is String) {
println("문자열이 아님")
}
when과 스마트 캐스트
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
fun describe(obj: Any): String {
return when (obj) {
is String -> "문자열, 길이: ${obj.length}"
is Int -> "정수: $obj"
is List<*> -> "리스트, 크기: ${obj.size}"
else -> "알 수 없음"
}
}
6. 상수
const val
아래 코드는 kotlin를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 컴파일 타임 상수
const val MAX_SIZE = 100
const val API_KEY = "your-api-key"
// 클래스 밖에서만 선언 가능
class Config {
companion object {
const val TIMEOUT = 5000
}
}
lateinit
아래 코드는 kotlin를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 나중에 초기화
class MyClass {
lateinit var name: String
fun init() {
name = "홍길동"
}
fun isInitialized() = ::name.isInitialized
}
lazy
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 처음 사용 시 초기화
val heavyObject: HeavyObject by lazy {
println("초기화 중...")
HeavyObject()
}
// 처음 접근 시에만 초기화됨
println(heavyObject.data)
7. 실전 예제
예제 1: 사용자 입력 처리
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
fun main() {
print("이름을 입력하세요: ")
val name = readLine() ?: "Guest"
print("나이를 입력하세요: ")
val ageInput = readLine()
val age = ageInput?.toIntOrNull() ?: 0
println("안녕하세요, $name님! (${age}세)")
}
예제 2: 안전한 계산
fun divide(a: Int, b: Int): Double? {
return if (b != 0) {
a.toDouble() / b
} else {
null
}
}
fun main() {
val result = divide(10, 0)
println("결과: ${result ?: "0으로 나눌 수 없음"}")
}
예제 3: 타입 변환 유틸리티
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
fun parseNumber(input: String): Int {
return input.toIntOrNull() ?: run {
println("잘못된 입력: $input")
}
}
fun main() {
println(parseNumber("123")) // 123
println(parseNumber("abc")) // 0
}
정리
핵심 요약
- val: 불변 (권장)
- var: 가변
- Nullable:
String?,?.,?:,!! - 타입 변환:
toInt(),toString() - 스마트 캐스트:
is연산자 - 지연 초기화:
lateinit,lazy