코틀린이 올해로 15살이 됐다. 처음에는 Java의 불편함을 줄이는 수단이었지만, 지금은 결제 시스템, 교통 티켓, 기내 엔터테인먼트, 세금 신고까지 실생활 깊숙이 파고들어 있다. AI가 코드 생성의 많은 부분을 담당하기 시작한 시점에서, 언어에 대한 신뢰는 오히려 더 중요해진다. AI가 생성한 코드를 담는 그릇이 안전하고 표현력 있어야 하기 때문이다.
KotlinConf'26 키노트에서 발표된 2.4.0 방향은 그 신뢰를 언어 수준에서 다지는 작업이다. 안드로이드 개발자 입장에서 주목할 세 가지를 짚어본다.
Context Parameters: API에서 "당연히 있어야 할 것"을 없애기
안드로이드 코드베이스에서 반복되는 패턴 중 하나가 있다. Logger, AnalyticsTracker, CoroutineDispatcher 같은 크로스커팅 관심사를 함수 파라미터로 계속 내려보내는 것이다.
suspend fun syncUserProfile(
userId: String,
dispatcher: CoroutineDispatcher,
logger: Logger,
tracker: AnalyticsTracker
): UserProfile {
logger.d("Syncing $userId")
tracker.track("sync_start")
return withContext(dispatcher) { /* ... */ }
}
파라미터 개수가 늘어날수록 핵심 로직이 묻힌다. Context parameters는 이 문제를 언어 수준에서 해결한다.
context(Logger, AnalyticsTracker, CoroutineDispatcher)
suspend fun syncUserProfile(userId: String): UserProfile {
d("Syncing $userId")
track("sync_start")
return withContext(this@CoroutineDispatcher) { /* ... */ }
}
호출부에서는 컨텍스트 객체를 with 블록으로 제공한다.
with(logger) {
with(tracker) {
with(Dispatchers.IO) {
syncUserProfile(userId)
}
}
}
핵심은 "이 함수는 이런 환경을 필요로 한다"는 사실이 타입 시스템으로 명시된다는 점이다. 암묵적 전역 상태나 DI 프레임워크 마법 없이도 의존성이 투명하게 드러난다. Hilt나 Koin을 완전히 대체하는 개념이 아니다. 라이브러리 내부 API나 도메인 레이어 설계에서 파라미터 폭발을 줄이는 데 적합하다.
Explicit Backing Fields: ViewModel 보일러플레이트의 끝
안드로이드 ViewModel을 작성할 때마다 이 패턴이 등장한다.
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
상태를 외부에 읽기 전용으로 노출하면서 내부에서만 변경할 수 있게 하는 관용구다. 기능은 맞지만 두 선언이 항상 쌍으로 다닌다. Explicit backing fields는 이 관계를 하나로 표현한다.
val uiState: StateFlow<HomeUiState>
field = MutableStateFlow(HomeUiState())
get() = field.asStateFlow()
field 키워드로 실제 저장 타입을 명시하고, get()에서 외부에 노출할 타입을 반환한다. 내부에서는 field로 직접 MutableStateFlow에 접근할 수 있다.
fun updateLoading(isLoading: Boolean) {
field.update { it.copy(isLoading = isLoading) }
}
단순히 코드 줄 수를 줄이는 게 아니라, "이 프로퍼티의 저장 타입과 공개 타입이 다르다"는 의도가 명확해진다. 리뷰어가 _state 선언과 state 선언을 찾아 연결할 필요가 없어진다.
Multi-Field Value Classes: 원시 타입 집착을 끊는 방법
현재 @JvmInline value class는 필드를 하나만 가질 수 있다. Money를 표현하려면 amount만 래핑할 수 있고, currency는 포함할 수 없다. 2.4.0 실험 기능으로 이 제약이 풀린다.
value class Money(val amount: Long, val currency: Currency)
value class Coordinate(val latitude: Double, val longitude: Double)
value class ColorToken(val light: Int, val dark: Int)
컴파일러가 equals(), hashCode(), toString()을 자동으로 생성하고, identity semantics가 없기 때문에 두 Money 인스턴스는 같은 값이면 항상 같다. 이름 기반 구조 분해도 기본 제공된다.
val price = Money(9900L, Currency.KRW)
val (amount, currency) = price // component 함수 정의 없이 동작
Long, String, Int로 도메인 개념을 표현하면 잘못된 타입끼리 섞일 때까지 컴파일러가 잡아주지 못한다. Value classes는 이 문제를 런타임 오버헤드 없이 해결한다. 단, 현재는 실험 단계이므로 프로덕션에 바로 적용하기보다 설계에 반영할 시점을 가늠하면서 따라가는 게 현실적이다.
Kotlin Toolchain: 생태계를 하나의 진입점으로
언어 기능 외에 발표된 Kotlin Toolchain은 빌드, 실행, 테스트, 포맷, 문서 생성을 단일 커맨드로 묶는 시도다. 내부적으로 Amper가 핵심 빌드 레이어를 담당하며, 향후 LSP 통합과 AI 스킬까지 포함할 계획이다.
멀티모듈 프로젝트에서 Gradle 빌드 스크립트 관리에 피로를 느끼고 있다면 주목할 방향이다. 아직 성숙 단계는 아니지만 코틀린 생태계가 "언어만 좋은 게 아니라 주변 도구도 일관된 경험을 줘야 한다"는 방향으로 수렴하고 있다는 신호다.
Context parameters와 explicit backing fields는 2.4.0에서 안정화된다. 지금 바로 사용하려면:
// build.gradle.kts
plugins {
kotlin("android") version "2.4.0"
}
Multi-field value classes는 실험적 컴파일러 플래그가 필요하다.
// build.gradle.kts
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xmulti-field-value-classes")
}
}
15년이 지난 언어가 "보일러플레이트 줄이기"가 아니라 "타입 시스템으로 의도를 표현하기"로 진화하고 있다. 안드로이드 코드베이스에서 가장 많이 반복되는 패턴들이 언어 수준에서 정리되기 시작했다.
※ 본 글은 정보 제공 목적이며 특정 제품·서비스의 추천이 아닙니다.
'안드로이드' 카테고리의 다른 글
| 크래시 리포트만 보고 있다면, 장애의 절반을 놓치고 있는 것이다 (0) | 2026.06.29 |
|---|---|
| list.map{}이 느린 건 CPU 때문이 아니다 (0) | 2026.06.26 |
| Compose에 Mesh Gradient가 생겼다, 단 함정이 있다 (0) | 2026.06.24 |
| Gradle이 재컴파일 범위를 결정하는 방법: ABI 해싱 완전 해부 (0) | 2026.06.22 |
| 2026년 안드로이드 앱에 LLM을 제대로 붙이는 법 (0) | 2026.06.16 |