이 가이드에서는 컴파일 시간 안전성을 달성하고 오타나 잘못된 인수 유형으로 인해 발생하는 런타임 비정상 종료를 방지하기 위해 문자열 기반 경로를 직렬화 가능한 Kotlin 유형으로 대체하는 과정을 설명합니다.
기본 요건
마이그레이션을 시작하기 전에 프로젝트가 다음 요구사항을 충족하는지 확인하세요.
- 탐색 버전: Jetpack 탐색 2.8.0 이상으로 업데이트
- Kotlin 직렬화 플러그인:
libs.versions.toml에 플러그인을 추가합니다.
[libraries]
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
- 최상위
build.gradle.kts및 모듈 수준build.gradle.kts에 종속 항목을 추가합니다.
1단계: 대상 정의
상수 경로 문자열을 @Serializable 객체 및 클래스로 바꿉니다.
- 인수가 없는 화면의 경우:
data object사용 - 인수가 있는 화면의 경우:
data class사용
이전 (문자열 기반):
const val ROUTE_HOME = "home"
const val ROUTE_PROFILE = "profile/{userId}"
After (타입 안전):
import kotlinx.serialization.Serializable
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
2단계: NavHost 구성 업데이트
composable 및 dialog 함수에서 새 일반 유형을 사용하도록 NavHost를 업데이트합니다.
변경 전:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(...) }
composable("profile/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId)
}
}
변경 후:
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(...)
}
composable<Profile> { backStackEntry ->
// The library automatically handles argument extraction
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(profile.userId)
}
}
3단계: 타입 안전 탐색 호출 구현
문자열 삽입 탐색 호출을 클래스 인스턴스로 바꿉니다.
변경 전:
navController.navigate("profile/user123")
변경 후:
navController.navigate(Profile(userId = "user123"))
4단계: ViewModel에서 인수에 액세스
ViewModel을 사용하는 경우 이제 SavedStateHandle에서 경로 객체를 직접 추출할 수 있습니다.
구현:
class ProfileViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
// Automatically parses arguments into the Profile class
private val profile = savedStateHandle.toRoute<Profile>()
val userId = profile.userId
}
5단계: (고급) 맞춤 유형 처리
기본형이 아닌 복잡한 데이터 클래스를 전달해야 하는 경우 맞춤 NavType를 정의해야 합니다.
- 맞춤 유형 만들기:
```kotlin
val SearchFilterType = object : NavType
(isNullableAllowed = false) { override fun get(bundle: Bundle, key: String): SearchFilter? = Json.decodeFromString(bundle.getString(key) ?: return null)
override fun parseValue(value: String): SearchFilter =
Json.decodeFromString(Uri.decode(value))
override fun put(bundle: Bundle, key: String, value: SearchFilter) {
bundle.putString(key, Json.encodeToString(value))
}
}
2. **Register it in the Graph**:
```kotlin
composable<Search>(
typeMap = mapOf(typeOf<SearchFilter>() to SearchFilterType)
) { ... }
권장사항 및 도움말
- 봉인된 계층 구조: 대규모 앱의 경우 봉인된 인터페이스나 클래스를 사용하여 경로를 그룹화하여 탐색 구조를 체계적으로 유지합니다.
- 객체 인스턴스: 파라미터가 없는 경로의 경우 불필요한 할당을 방지하기 위해 항상
class대신object를 사용합니다. - Null 허용 유형: 새 API는 null 허용 유형 (예:
data class Search(val query: String?))을 지원하고 기본값을 자동으로 제공합니다. - 테스트: UI 테스트 중에
navController.currentBackStackEntry?.hasRoute<T>()를 사용하여 타입 안전 방식으로 현재 대상을 확인합니다.