Navigation 3으로 이전

Navigation 3은 Jetpack Compose가 탐색 상태를 처리하는 방식의 근본적인 변화를 나타내며 Navigation 2에 비해 상당한 아키텍처 이점을 제공합니다.

Wear Compose 앱을 Navigation 2에서 Navigation 3으로 이전하는 데 필요한 아키텍처 변경사항과 단계를 알아봅니다.

Navigation 3의 주요 이점

  • 직접 백 스택 제어: NavBackStack은 기본적으로 NavKey 객체의 변경 가능한 목록으로, 사용자가 방문한 화면의 기록을 나타냅니다. Kotlin MutableList (add, removeLast, clear)와 마찬가지로 정확하게 제어합니다. 목록을 직접 조작하여 앞으로 이동하는 키를 추가하거나 뒤로 이동하는 키를 삭제하는 등의 탐색 작업을 실행합니다.
  • Compose 우선 설계: 백 스택은 표준 관찰 가능한 상태로 모델링됩니다. 탐색 기록을 수정하는 것은 다른 Compose 상태를 업데이트하는 것과 정확히 동일하게 동작하며, 현재 화면을 표시하기 위해 리컴포지션을 자동으로 트리거합니다.
  • 기본적으로 유형 안전: 문자열 기반 경로가 완전히 삭제됩니다. 탐색은 직렬화 가능한 데이터 객체와 데이터 클래스를 활용합니다.
  • 분리된 프레젠테이션 (장면 전략): UI 전환 레이어 (NavDisplaySwipeDismissableSceneStrategy)는 상태 추적 (NavBackStack)과 완전히 분리되어 기본 제공 Wear OS 탐색 전환을 더 간단하게 통합할 수 있습니다.

이전 단계

1. 종속 항목 업데이트

이전 androidx.wear.compose:compose-navigation 종속 항목을 삭제하고 Kotlin 직렬화 지원과 함께 새로운 분할 Navigation 3 종속 항목을 도입합니다.

삭제:

implementation("androidx.wear.compose:compose-navigation:...")

추가:

implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...")      // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin

2. 대상을 업데이트하여 NavKey 구현

Navigation 2에서는 라우팅에 문자열 또는 일반 객체를 사용했을 수 있습니다. Navigation 3에서는 구현해야 합니다. NavKey 마커 인터페이스를 구현하고 모든 화면 객체에 @Serializable을 주석 처리해야 합니다.

이것이 필요한 이유는 무엇인가요? 프로세스 종료 시 백 스택을 저장하고 복원할 수 있도록 기본 navigation3-runtimekotlinx-serialization을 사용하여 상태를 직렬화합니다.

이전 (Navigation 2 - 일반 유형 안전 경로):

sealed class Nav2Screen {
    data object Landing : Nav2Screen()
    data object List : Nav2Screen()
}

이후 (Navigation 3 - NavKey + 직렬화 가능):

@Serializable
sealed interface MigrationScreen : NavKey {
    @Serializable
    data object Landing : MigrationScreen

    @Serializable
    data object List : MigrationScreen
}

3. 라우팅 로직 (NavController에서 NavBackStack으로) 바꾸기

NavControllerNavBackStack을 통해 초기화된 rememberNavBackStack으로 바꿉니다. 또한 Wear OS에 맞게 SwipeDismissableSceneStrategy를 인스턴스화해야 합니다.

이전 (Navigation 2):

val navController = rememberSwipeDismissableNavController()

이후 (Navigation 3):

val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey)
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

4. NavHostNavDisplayentryProvider DSL로 바꾸기

NavHost 컨테이너와 내부 composable("route") { ... } 빌더 DSL은 NavDisplayentryProvider { entry<Key> { ... } } DSL로 바뀝니다.

이전 (Navigation 2):

SwipeDismissableNavHost(navController = navController, startDestination = "menu") {
    composable("menu") {
        GreetingScreen(
            onShowList = { navController.navigate("list") }
        )
    }
    composable("list") {
        ListScreen()
    }
}

이후 (Navigation 3):

NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        entry<MigrationScreen.Landing> {
            GreetingScreen(
                onShowList = { backStack.add(MigrationScreen.List) }
            )
        }
        entry<MigrationScreen.List> {
            ListScreen()
        }
    }
)