Kotlin을 공부중인데 val에 대해서 글을 읽던 중 엥? 이게 무슨 말이지 하는 부분이 있었다.
분명 "val 로 선언하면 값을 바꿀 수 없다" 라고 봤는데, val로 선언했는데 객체 내부 값이 변하는걸 목격했다.
분명히 고정된 값(val)인데 왜 내부 데이터는 바뀌는건지 혼란스러웠다.
이건 Kotlin이 값을 불변하게 만든다고 오해한 데서 시작된 착각이라는 것을 알았다.
진짜로 불변(Immutable)한 것은 "참조(reference)" 이지, "객체의 상태(state)" 가 아니다.
1. Kotlin의 val 과 var 기본 개념
먼저, Kotlin의 val 과 var 는 변수 선언 방식이다.
키워드 | 의미 |
val | 읽기 전용(Read-only) 참조 |
var | 읽기/쓰기(Read-Write) 참조 |
- val 은 참조를 변경할 수 없다.
- var 은 참조를 변경할 수 있다.
즉, val 은 "이 참조가 다른 객체를 가리키지 않도록 고정"하는 것이다. "참조하는 객체 재부"는 건들 수 있다.
val list = mutableListOf(1, 2, 3)
list.add(4) // 가능
list.remove(2) // 가능
// list = mutableListOf(5, 6, 7) // ❌ 에러! 참조 변경 불가
위 코드에서 알아볼 수 있다.
- list 가 가리키는 리스트 객체는 수정 가능하다.
- 그러나 list 자체를 다른 리스트로 바꿀 수 없다.
var 은 참조 변경이 가능하다
var list = mutableListOf(1, 2, 3)
list = mutableListOf(10, 20, 30) // 가능
오호..이제 알겠다. 근데 왜 이런 설계를 했을까???
2. val 설계 이유
Kotlin이 val 을 참조 고정만 보장하고, 객체 불변성을 강제하지 않는 이유는 다음과 같다.
이유 1 : 유연성과 기능
- 객체를 통째로 새로 생성하는 것보다, 기존 객체를 수정하는 것이 빠를 때가 많다.
- 특히 컬렉션 같은 경우 "수정 가능한 컬렉션 (mutableListOf)" 은 효율적이다.
이유 2: Kotlin의 철학
- Kotlin은 "개발자에게 선택권을 준다" 는 철학을 지향한다.
- 불변 객체를 원하면, 개발자가 immutable한 자료구조를 선택해야 한다.
Kotlin은 "불면(immutable)"을 강제하지 않는다. 읽기 전용 참조(read-only reference)만 보장한다.
3. Mutable 과 Immutable 객체
mutable = 내부 상태를 바꿀 수 있다.
immutable = 내부 상태를 바꿀 수 없다.
구분 | 설명 | 예시 |
Mutable 객체 | 객체 내부 데이터 변경 가능 | mutableListOf, HashMap 등 |
Immutable 객체 | 객체 내부 데이터 변경 불가 | listOf, Map (읽기 전용 View) |
Kotlin의 listOf() 로 만든 리스트도 사실은 완전한 immutable은 아니다. 완전히 불변한 컬렉션을 원하면 별도로 관리해야 한다.
예: Collecitons.unmodifiableList(), 또는 직접 만드는 데이터 클래스
즉 비유를 하자면
val 은 집주소를 고정하는 것이다. 집 주소를 못 바꾸지만, 집안 가구 배치나 이런건 마음대로 바꿀 수 있지 않은가.
val 집 = MyHouse()
집.소파 = "새 소파로 교체" // OK
// 집 = 다른집() // ❌ 참조 변경은 불가
4. 데이터 클래스와 불변성
Kotlin에서는 데이터 클래스를 많이 사용한다.
data class Person(var name: String, var age: Int)
val 로 선언해도 name, age는 변경 가능하다.
val person = Person("Alice", 25)
person.age = 26 // 내부 필드 변경 가능
- person 이라는 참조는 고정된다
- 하지만 person 객체 내부 필드(age)는 변경 가능하다.
정말 진짜 불변성을 원한다면 다음과 같이 만들 수 있다.
1. data class를 val 프로퍼티로만 만든다.
data class ImmutablePerson(val name: String, val age: Int)
이제 name, age를 바꿀 수 없다.
2. 불변 컬렉션을 사용한다.
val list = listOf(1, 2, 3)
// list.add(4) // ❌ 컴파일 에러
주의할 점
상황 | 주의해야 할 점 |
API 리턴 타입을 val로 선언했지만 내부 객체가 Mutable인 경우 | 외부에서 상태가 변조될 수 있다. |
글로벌 상태를 val로만 선언하고 안심하는 경우 | Thread-safety는 별개 문제이다. |
- val 은 객체 상태를 보호해주지 않는다.
- 불변성을 원하면 객체 설계 자체를 immutable하게 헤야 한다.
'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글
Spring Bean? (5) | 2025.06.12 |
---|---|
DB Connection Pool 고갈이 되면 무슨 일이 벌어질까? (0) | 2025.05.05 |
Spring Boot 의존성 주입 (Dependency Injection) (2) | 2025.04.16 |
Spring Boot와 WebSocket 을 이용한 실시간 통신 시스템 구축해보기 (1) | 2025.04.07 |
Jar 파일과 War 파일의 차이점 및 활용법 (0) | 2025.03.26 |