--- id: android-navigation-compose title: Android Navigation (Compose) — 타입 안전 라우팅 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [android, navigation, compose, vibe-coding] tech_stack: { language: "Kotlin / Navigation-Compose 2.8+", applicable_to: ["Android"] } applied_in: [] aliases: [NavController, NavGraph, type-safe routes, deep link] --- # Android Navigation (Compose) > Navigation-Compose 2.8+ 의 **타입 안전 routes** (Kotlin Serialization). 옛 string route 시대는 끝. **NavGraph 분할 + 단방향 dependency** 가 modular 앱의 표준. ## 📖 핵심 개념 - 각 destination = `@Serializable` data class. - NavController.navigate(Object) — 타입 안전. - 인자 자동 SavedStateHandle 주입. - Deep link, animation, nested graph 모두 지원. ## 💻 코드 패턴 ### 타입 안전 destination ```kotlin @Serializable data object HomeRoute @Serializable data class ProfileRoute(val userId: String) @Serializable data class OrderRoute(val orderId: String, val highlight: Boolean = false) ``` ### NavHost + composable ```kotlin @Composable fun AppNav(nav: NavHostController) { NavHost(navController = nav, startDestination = HomeRoute) { composable { HomeScreen(onUserClick = { id -> nav.navigate(ProfileRoute(id)) }) } composable { entry -> val args = entry.toRoute() ProfileScreen(userId = args.userId, onBack = { nav.popBackStack() }) } composable { entry -> val args = entry.toRoute() OrderScreen(args.orderId, args.highlight) } } } ``` ### ViewModel 자동 인자 주입 ```kotlin @HiltViewModel class ProfileViewModel @Inject constructor( savedState: SavedStateHandle, private val repo: UserRepo, ) : ViewModel() { private val args: ProfileRoute = savedState.toRoute() val userFlow = repo.observe(args.userId) } ``` ### Nested graph (모듈) ```kotlin fun NavGraphBuilder.authGraph(nav: NavController) { navigation(startDestination = LoginRoute) { composable { LoginScreen(onSuccess = { nav.navigate(HomeRoute) { popUpTo { inclusive = true } } }) } composable { SignupScreen() } } } @Serializable data object AuthGraph @Serializable data object LoginRoute @Serializable data object SignupRoute ``` ### Deep link ```kotlin composable( deepLinks = listOf(navDeepLink(basePath = "https://example.com/orders")) ) { ... } ``` URL `https://example.com/orders/123?highlight=true` → OrderRoute(orderId="123", highlight=true). ### Pop / 백스택 관리 ```kotlin nav.navigate(HomeRoute) { popUpTo { inclusive = true } // login graph 모두 pop launchSingleTop = true // 같은 destination 재진입 안 함 } nav.popBackStack() nav.popBackStack(route = HomeRoute, inclusive = false) // 특정 destination 까지 pop ``` ## 🤔 의사결정 기준 | 상황 | 권장 | |---|---| | 신규 Android Compose | navigation-compose 2.8+ + 타입 안전 routes | | 큰 앱 모듈화 | nested graph per feature module | | Deep link / 외부 호출 | deepLinks block | | Multi-platform (KMM) | Decompose / Voyager | | Tab + per-tab back stack | rememberNavController 별 또는 BottomNav + saveState | ## ❌ 안티패턴 - **String route + manual encoding**: 옛 패턴. 2.8+ 의 typed routes. - **모든 destination 한 NavGraphBuilder**: 거대 파일. nested graph 로 모듈 분리. - **navigate 후 popBackStack 잊음**: 백스택 누적. - **ViewModel 에 NavController 주입**: lifecycle 불일치 + 테스트 어려움. Composable 에서 callback. - **Activity 가 NavController 보유 + Fragment 가 별도**: 동기화 깨짐. - **deeplink path 안에 user id**: 인증 안 된 사용자가 직접 호출 → 권한 검증 필수. - **animation 무시**: 기본 fade 만 — UX 평범. enterTransition / exitTransition. ## 🤖 LLM 활용 힌트 - @Serializable data class destination + composable + entry.toRoute(). - ViewModel 은 SavedStateHandle.toRoute() 로 인자. ## 🔗 관련 문서 - [[Android_Compose_State_Hoisting]] - [[Android_Modularization]]