# Kotlin 2.3 Android 면접 완벽 가이드: 이름 기반 구조 분해, KMP, 핵심 질문 정리 2026 > 2026년 Android 개발자 면접에서 출제되는 Kotlin 2.3 신기능을 상세히 분석합니다. 이름 기반 구조 분해, Kotlin Multiplatform, 컨텍스트 파라미터, Flow와 코루틴 코드 예제를 포함합니다. - Published: 2026-05-06 - Updated: 2026-05-06 - Author: SharpSkill - Tags: kotlin, android, interview, kotlin-multiplatform, coroutines - Reading time: 12 min --- 2026년 현재, Kotlin 2.3 면접 질문은 Android 개발자 채용에서 가장 많이 검색되는 주제 중 하나로 자리잡았습니다. 이름 기반 구조 분해 선언, 안정화된 컨텍스트 파라미터, 성숙한 Kotlin Multiplatform 생태계 등 면접관들은 후보자에게 이러한 기능에 대한 깊은 이해를 기대합니다. > **Kotlin 2.3의 주요 변경 사항** > > 2026년 3월에 출시된 Kotlin 2.3.20은 이름 기반 구조 분해 선언, 컨텍스트 파라미터의 오버로드 해소 변경, C 및 TypeScript와의 KMP 상호 운용성 개선을 도입했습니다. 이러한 기능은 시니어 Android 면접에서 자주 출제됩니다. ## 이름 기반 구조 분해: Kotlin 2.3 면접에서 가장 빈출되는 문제 Kotlin 2.3 이전에는 구조 분해 선언이 위치 기반 `componentN()` 함수에 의존했습니다. 이로 인해 데이터 클래스의 프로퍼티 순서를 변경하면 기존 구조 분해 코드가 조용히 깨지는 잘 알려진 함정이 존재했습니다. 전형적인 면접 문제는 다음과 같은 시나리오를 제시합니다: ```kotlin // User.kt data class User(val username: String, val email: String) fun main() { val user = User("alice", "alice@example.com") // Position-based: order matters, not names val (email, username) = user println(email) // Prints "alice" -- wrong! println(username) // Prints "alice@example.com" -- wrong! } ``` Kotlin 2.3.20은 이름 기반 구조 분해를 통해 이 문제를 해결했습니다. 컴파일러에는 동작을 제어하는 세 가지 모드가 있습니다: ```kotlin // build.gradle.kts kotlin { compilerOptions { // Mode 1: Explicit syntax only freeCompilerArgs.add("-Xname-based-destructuring=only-syntax") // Mode 2: Warns on name mismatch // freeCompilerArgs.add("-Xname-based-destructuring=name-mismatch") // Mode 3: Full name-based by default // freeCompilerArgs.add("-Xname-based-destructuring=complete") } } ``` `complete` 모드에서는 소괄호가 이름 기반 매칭을 사용하고, 대괄호는 위치 기반 동작을 유지합니다: ```kotlin // NameBasedDestructuring.kt data class User(val username: String, val email: String) fun main() { val user = User("alice", "alice@example.com") // Name-based: matches by property name val (email, username) = user println(email) // "alice@example.com" -- correct println(username) // "alice" -- correct // Position-based: square brackets preserve old behavior val [first, second] = user println(first) // "alice" (component1) println(second) // "alice@example.com" (component2) } ``` 면접에서 높은 평가를 받는 답변은 세 가지 모드를 모두 설명하고 각각의 적용 시나리오를 명확히 합니다. `only-syntax`는 점진적 마이그레이션에, `name-mismatch`는 기존 코드의 버그 탐지에, `complete`는 신규 프로젝트에 적합합니다. ## Android 개발자를 위한 Kotlin Multiplatform 면접 질문 KMP는 실험 단계를 벗어나 본격적인 프로덕션 환경에 진입했습니다. Google은 Android와 iOS 간 비즈니스 로직 공유를 공식적으로 지원하며, 대부분의 신규 멀티플랫폼 프로젝트는 공유 코어 아키텍처를 기본으로 채택하고 있습니다. 일반적인 면접 질문은 "공유 네트워킹 모듈을 KMP로 어떻게 구성하겠습니까?"라는 형태입니다: ```kotlin // shared/src/commonMain/kotlin/com/app/network/ApiClient.kt import io.ktor.client.* import io.ktor.client.request.* import io.ktor.client.statement.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable data class UserProfile( val id: String, val displayName: String, val avatarUrl: String? ) // Expect/actual pattern for platform-specific HTTP engine expect fun createHttpClient(): HttpClient class ApiClient { private val client = createHttpClient() private val json = Json { ignoreUnknownKeys = true } // Suspend function shared across Android and iOS suspend fun fetchProfile(userId: String): UserProfile { val response = client.get("https://api.example.com/users/$userId") return json.decodeFromString(response.bodyAsText()) } } ``` ```kotlin // shared/src/androidMain/kotlin/com/app/network/HttpClient.android.kt import io.ktor.client.* import io.ktor.client.engine.okhttp.* // Android uses OkHttp engine actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) { engine { config { retryOnConnectionFailure(true) } } } ``` 면접관은 후보자가 expect/actual 메커니즘, 플랫폼별 의존성, 공유 코드와 플랫폼 코드의 경계를 이해하고 있는지 평가합니다. 또 다른 빈출 질문은 [Swift Export](https://kotlinlang.org/docs/whatsnew2320.html)에 관한 것입니다. 2026년에는 Swift Export가 크게 발전하여, Kotlin의 suspend 함수가 Swift의 async/await에 직접 매핑되고, sealed class가 Swift의 associated value를 가진 enum으로 매핑됩니다. ## 컨텍스트 파라미터와 오버로드 해소 변경 컨텍스트 파라미터는 더 이상 사용되지 않는 컨텍스트 리시버 API를 대체합니다. Kotlin 2.3.20에서는 오버로드 해소에 파괴적 변경이 도입되었습니다. 컨텍스트 파라미터가 있는 선언은 더 이상 컨텍스트 파라미터가 없는 선언보다 더 구체적인 것으로 간주되지 않습니다. ```kotlin // ContextParameters.kt class Logger { fun info(msg: String) = println("INFO: $msg") } class TransactionScope { fun execute(block: () -> Unit) = block() } // Function with context parameter context(logger: Logger) fun saveUser(id: Int) { logger.info("Saving user $id") } // Overload without context -- now ambiguous in 2.3.20 fun saveUser(id: Int) { println("Saving user $id (no logger)") } fun main() { val logger = Logger() // This now produces an ambiguity error in Kotlin 2.3.20 // context(logger) { saveUser(1) } // Fix: rename or qualify the context-aware variant } ``` 면접관은 이 주제를 통해 후보자가 파괴적 변경에 대한 인식과 마이그레이션 전략을 갖추고 있는지 평가합니다. 핵심 요점은 암묵적 컨텍스트 파라미터 해소를 통한 비컨텍스트 오버로드의 섀도잉이 더 이상 작동하지 않는다는 것입니다. ## 코루틴과 Flow: Android 면접의 최다 출제 영역 Kotlin 2.3에서 새로운 기능이 추가되었음에도 불구하고, 코루틴과 Flow는 여전히 가장 많이 테스트되는 영역입니다. 면접에서는 일반적으로 콜드 플로우와 핫 플로우의 차이, 구조화된 동시성, 취소에 대해 심층적으로 질문합니다. ```kotlin // FlowComparison.kt import kotlinx.coroutines.* import kotlinx.coroutines.flow.* // Cold Flow: emits only when collected fun userUpdates(): Flow = flow { println("Flow started") // Runs per collector emit("User logged in") delay(1000) emit("User updated profile") } // Hot Flow: emits independently of collectors class UserRepository { // StateFlow holds the latest value private val _state = MutableStateFlow("idle") val state: StateFlow = _state.asStateFlow() // SharedFlow broadcasts to multiple collectors private val _events = MutableSharedFlow() val events: SharedFlow = _events.asSharedFlow() suspend fun login() { _state.value = "loading" delay(500) _state.value = "authenticated" _events.emit("login_success") } } ``` 후속 질문에서는 Android에서 Flow를 안전하게 수집하는 방법이 자주 출제됩니다: ```kotlin // UserViewModel.kt import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch class UserViewModel(private val repo: UserRepository) : ViewModel() { // Expose state to Compose UI val uiState: StateFlow = repo.state .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = "idle" ) init { // Collect events safely within ViewModel scope viewModelScope.launch { repo.events.collect { event -> // Handle one-time events println("Event received: $event") } } } } ``` `WhileSubscribed(5000)` 전략은 마지막 구독자가 사라진 후 5초 동안 업스트림을 활성 상태로 유지합니다. 이를 통해 화면 회전과 같은 구성 변경 시 불필요한 재시작을 방지할 수 있습니다. ## Sealed 클래스와 완전한 when 식: 면접에서의 패턴 매칭 Sealed 클래스는 거의 모든 Android 면접에서 등장합니다. 전형적인 질문은 UI 상태를 모델링하는 것입니다: ```kotlin // UiState.kt sealed interface UiState { data object Loading : UiState data class Success(val data: T) : UiState data class Error(val message: String, val retry: (() -> Unit)? = null) : UiState } // Usage in ViewModel fun loadUsers(): Flow>> = flow { emit(UiState.Loading) try { val users = apiClient.fetchUsers() emit(UiState.Success(users)) } catch (e: Exception) { emit(UiState.Error( message = e.localizedMessage ?: "Unknown error", retry = { /* trigger reload */ } )) } } ``` Kotlin에서 sealed 타입에 대한 `when` 식은 컴파일 시점에 완전성이 검증됩니다. 새로운 하위 타입을 추가하면 모든 곳에서 처리가 강제되므로, 런타임 버그의 한 범주 전체가 제거됩니다. 면접관은 특히 후보자가 이 패턴에서 `sealed class`가 아닌 `sealed interface`를 사용하는지 확인합니다. 인터페이스가 더 유연한 계층 구조를 가능하게 하기 때문입니다. ## Kotlin/Wasm과 크로스 플랫폼 컴파일 타겟 Kotlin/Wasm은 Beta 상태에 도달하여 브라우저에서 네이티브에 가까운 성능을 제공합니다. Android 개발자의 일상 업무에서 직접적으로 다루는 경우는 아직 드물지만, 웹+모바일 제품을 보유한 기업의 면접에서는 출제될 수 있습니다. 면접에서 알아야 할 핵심 사항: - Kotlin 2.3.20은 JS String builtins를 통해 Wasm에서 문자열 보간 성능을 4.6배 향상 - 바이너리 크기가 약 5% 감소 - 클린 빌드가 65% 빨라지고, 증분 빌드가 21% 빨라짐 - `@nativeInvoke` 어노테이션으로 Kotlin 객체를 JavaScript 함수로 동작시킬 수 있음 Kotlin/JS에서도 TypeScript에서 Kotlin 인터페이스를 구현하는 기능이 추가되어 이전의 제한이 해소되었습니다. SWC 트랜스파일 지원과 결합하여, Kotlin은 단일 코드베이스에서 Android, iOS(KMP 경유), 웹(Wasm/JS 경유), 서버 사이드까지 모든 타겟을 커버할 수 있게 되었습니다. ## 스코프 함수: 보기보다 까다로운 면접 필터 문제 스코프 함수(`let`, `run`, `with`, `apply`, `also`)는 간단해 보이지만 숙련된 Kotlin 개발자를 구분하는 효과적인 지표입니다. 전형적인 면접 질문은 "`let`과 `apply`를 어떻게 구분하여 사용합니까?"입니다. ```kotlin // ScopeFunctions.kt data class Request( var url: String = "", var method: String = "GET", var headers: MutableMap = mutableMapOf(), var body: String? = null ) fun buildRequest(): Request { // apply: configure an object, returns the object return Request().apply { url = "https://api.example.com/users" method = "POST" headers["Content-Type"] = "application/json" body = "{\"name\": \"Alice\"}" } } fun processNullable(input: String?) { // let: transform nullable, returns lambda result val length = input?.let { value -> println("Processing: $value") value.trim().length } ?: 0 // also: side effects, returns the original object input?.also { println("Logging input: $it") } // run: execute a block with receiver, returns lambda result val result = input?.run { // 'this' is the string uppercase().take(10) } } ``` 판단 기준은 다음과 같습니다: `apply`는 객체 구성(리시버 반환), `let`은 null 안전 변환(람다 결과 반환), `also`는 부수 효과(리시버 반환), `run`은 리시버 컨텍스트에서 결과를 계산할 때 사용합니다. ## Null 안전성과 더블 뱅의 위험성 Null 안전성은 면접의 기본 주제로 확고히 자리잡고 있습니다. "왜 `!!`는 위험한가?"라는 질문은 후보자가 Kotlin의 타입 시스템을 근본적으로 이해하고 있는지 확인하는 것입니다. ```kotlin // NullSafety.kt fun findUser(id: String): User? { // Returns null if user not found return database.queryUser(id) } // Bad: !! defeats the purpose of null safety fun riskyApproach(id: String) { val user = findUser(id)!! // NullPointerException if null println(user.username) } // Good: handle null explicitly fun safeApproach(id: String) { val user = findUser(id) ?: run { println("User not found") return } println(user.username) } // Good: provide default with Elvis operator fun displayName(id: String): String { return findUser(id)?.username ?: "Anonymous" } ``` 완전한 답변에서는 `!!`가 테스트 코드나 플랫폼 상호 운용 경계처럼 null이 절대 발생할 수 없는 상황에서만 사용되어야 한다고 언급합니다. ## 결론 - Kotlin 2.3.20의 이름 기반 구조 분해는 위치 의존적 버그를 제거합니다. 세 가지 컴파일러 모드(`only-syntax`, `name-mismatch`, `complete`)를 숙지해야 합니다 - KMP는 2026년에 프로덕션 수준으로 안정화되었습니다. expect/actual, 공유 모듈 아키텍처, Swift Export 상호 운용에 대한 질문이 예상됩니다 - 컨텍스트 파라미터가 컨텍스트 리시버를 대체했습니다. 2.3.20의 오버로드 해소 변경은 자주 출제되는 함정 문제입니다 - 코루틴과 Flow는 Android 면접을 지배합니다. `StateFlow` 대 `SharedFlow`, `WhileSubscribed`, 구조화된 동시성을 마스터해야 합니다 - Sealed 인터페이스는 컴파일 시점 완전성 검사를 통한 UI 상태 모델링에 적합합니다. `sealed class`보다 `sealed interface`를 선호해야 합니다 - 스코프 함수에는 명확한 판단 기준이 필요합니다: 리시버 대 인수, 반환값 대 리시버 - Null 안전성은 타협할 수 없는 항목입니다. `!!`가 타입 시스템을 훼손하는 이유를 설명하고 대안을 제시할 수 있어야 합니다 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/android/kotlin-2-3-interview-questions