이동하면서 백스택을 걷어내는 옵션.
// 로그인 성공 → 홈으로. 뒤로가기로 로그인에 돌아가면 안 된다
navController.navigate(Home) {
popUpTo(Login) { inclusive = true } // Login 까지 포함해 제거
}
- inclusive = false — 그 목적지 '위' 만 걷어낸다 (목적지는 남는다)
- inclusive = true — 그 목적지까지 함께 걷어낸다
왜 필요한가
navigate 만 쓰면 스택이 계속 쌓인다
- [Login] → [Login, Home]
- 뒤로가기 → 로그인 화면으로 돌아간다. 이미 로그인했는데
스플래시 · 온보딩 · 로그인은 전부 popUpTo 가 필요하다
하단 탭 — 표준 조합
navController.navigate(route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true // 떠나는 탭의 상태를 저장
}
launchSingleTop = true // 같은 탭을 다시 눌러도 쌓지 않는다
restoreState = true // 돌아오면 스크롤 위치까지 복원
}
이 세 줄이 없으면
- 탭을 오갈 때마다 스택이 무한히 쌓인다
- 뒤로가기를 20번 눌러야 앱이 닫힌다
- 탭을 다시 열 때마다 스크롤이 맨 위로 간다
launchSingleTop
맨 위에 같은 목적지가 있으면 새로 쌓지 않고 재사용한다
알림을 연달아 누르거나 버튼을 빠르게 두 번 누를 때 같은 화면이 겹겹이 쌓이는 것을 막는다
스택을 직접 확인한다
// 디버깅용 — 현재 백스택을 로그로 본다
navController.addOnDestinationChangedListener { c, d, _ ->
Log.d("NAV", c.currentBackStack.value.map { it.destination.route }.toString())
}
popBackStack의 반환값
val popped = navController.popBackStack()
if (!popped) finish() // 스택이 비었으면 Activity 를 닫는다
popBackStack 은 걷어낼 것이 없으면 false 를 반환한다 무시하면 "뒤로가기가 아무 반응이 없는" 화면이 된다
면접 함정
- ❌ "popUpTo는 화면을 지운다" → 백스택 엔트리를 제거한다.
saveState = true면 상태는 보관된다. - ❌ "restoreState만 켜면 복원된다" →
saveState와 짝이어야 한다. 한쪽만 켜면 동작하지 않는다.
결과를 안전하게 되돌려 주기
// 보내는 쪽 (선택 화면)
navController.previousBackStackEntry?.savedStateHandle?.set("picked", id)
navController.popBackStack()
// 받는 쪽 — 한 번 소비하고 지운다
val entry = navController.currentBackStackEntry
LaunchedEffect(entry) {
entry?.savedStateHandle?.getStateFlow<Long?>("picked", null)?.collect { id ->
if (id != null) { apply(id); entry.savedStateHandle["picked"] = null }
}
}
null 로 되돌리지 않으면 화면에 돌아올 때마다 같은 결과가 다시 적용된다 = 일회성 이벤트를 상태에 담았을 때와 똑같은 함정
조건부 시작 화면
// ❌ startDestination 을 조건으로 바꾸면 백스택이 꼬인다
NavHost(startDestination = if (loggedIn) Home else Login)
// ✅ 시작은 고정하고 이동으로 푼다
NavHost(startDestination = Splash) {
composable<Splash> {
LaunchedEffect(loggedIn) {
navController.navigate(if (loggedIn) Home else Login) {
popUpTo(Splash) { inclusive = true }
}
}
}
}
startDestination 이 런타임에 바뀌면 NavHost 가 재생성되며 이미 쌓인 백스택과 어긋난다 — 로그인/로그아웃 시 흔한 버그