기존 Nav2의 한계
- 백 스택 상태를 간접적으로 관찰
- 애플리케이션 상태가 일치하지 않을 수 있음
- Nav2의 NavHost는 단일 대상, 즉 백 스택의 최상위 대상만 표시
- 태블릿 등 대형 화면에서 적응형 레이아웃 구현에 한계가 있음
- KMP(Kotlin Multiplatform)와 대형 화면에 적용하기 어려움

선언형 UI에 적합한 네비게이션 멀티 플랫폼 지원 강화를 위한 유연한 아키텍처가 필요해졌다
Compose 패러다임 변화에 대응하자 -> Nav3
Navigation 3
2025년 5월 20일 안드로이드 공식 블로그에 zpac navigation 3가 공식적으로 발표 되었다
가장 큰 특징은 공식문서에서 다음 6가지로 소개하고 있다
- 백 스택을 완전히 제어할 수 있다는 점
- 대상 간의 이동이 간단해 졌다는 점
- 백 스택 변경 사항 + 애니메이션에 따라 자동 UI 업데이트
- 백 스택에 어떠한 항목이 있을 때 그 항목의 상태를 유지할 수 있는 범위
- 여러 대상을 동시에 표시하고 레이아웃 간 전환이 유연해진 적응형 레이아웃 시스템
- 상위 레이아웃과 콘텐츠가 통신하는 메커니즘
이 글에서는 우선 Key 관리, 백 스택에 대한 공식문서를 정리해보았다

네비게이션 3에서는 백 스택에 실제 콘텐츠가 포함되지 않는다.
그 대신, Key를 통해 콘텐츠 참조를 하게 된다 이 Key는 직렬화 가능한 데이터 클래스로, 어떤 유형이든 될 수 있다
백 스택 만들기
네비게이션 3 API의 핵심은 백 스택을 직접 관리한다는 점이다.
- 백 스택은 스냅샷 상태 백업 List<T>
- Push로 목록 끝에 항목을 추가하고 Pop으로 항목을 삭제한다
- 백 스택을 관찰하고 NavDisplay를 사용해서 UI에 상태를 반영한다
// 콘텐츠를 식별하는 키를 정의
data object MashUpList
data class MashUpDetail(val memberId: String)
@Composable
fun MyApp() {
// 앱이 시작해야 할 키를 지정하여 백 스택을 생성
val backStack = remember { mutableStateListOf<Any>(MashUpList) }
/*
UI 변경 사항을 반영하도록 NavDisplay에 백 스택 공급
키를 백 스택에 푸시하면 앞으로 탐색하여 네비게이션 상태 변화가 반영됨
키를 백 스택에 팝 하면 뒤로 탐색하여 네비게이션 상태 변화가 반영됨
*/
backStack.add(MashUpDetail("아린"))
backStack.removeLastOrNull()
}
백 스택 표시
백 스택은 앱의 탐색 상태를 나타내며, 백 스택이 변경될 때마다 새로운 백 스택 상태가 앱 UI에 반영된다
네비게이션 3의 NavDisplay로 백 스택을 관찰하며, 이에 따라 UI가 업데이트 된다
- 백 스택 : SnapshotStateList<T> 유형
- SnapshotStateList<T>란, 변경될 때마다 UI를 자동으로 그려주는 List이다
- 여기서 T는 백 스택에 저장되는 keys의 유형으로, Any를 사용하거나 자체 키를 제공할 수 있다
- 관찰 가능한 List이므로, 백 스택이 변경될 때마다 NavDisplay가 이를 관찰하여 리컴포지션을 트리거하고, 새로운 UI를 나타낸다
- 백 스택 키를 NavEntry로 변환하는 entryProvider
- 백 스택에는 실제 화면 대신, 화면을 식별하는 Key만 저장된다
- UI에 화면을 나타내려면 이 Key를 실제로 렌더링 하는 NavEntry로 바꿔야 하고, 이 작업을 entryProvider가 담당한다
즉, NavEntry가 실제 UI와 metadata를 담고 있다
- 필요에 따라 onBack 매개 변수에 람다를 제공하여, Back 이벤트를 트리거할 때 호출한다
data object Home
data class MashUp(val memberId: String)
@Composable
fun NavExample() {
val backStack = remember { mutableStateListOf<Any>(Home) }
NavDisplay(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
entryProvider = {
when (key) {
is Home -> NavEntry(key) {
Column {
Button(
onClick = {
backStack.add(MashUp("아린"))
}
) {
Text("안드")
}
}
}
is MashUp -> NavEntry(key) {
Text(key.memberId)
}
else -> NavEntry(Unit) { Text("없는 경로입니다") }
}
}
)
}
콘텐츠 키 확인
entryProvider 함수란?
key를 수락하고 해당 key의 NavEntry를 반환하는 함수
일반적으로 NavDisplay를 만들면서 람다의 매개변수로 정의된다
NavEntry 안에는 다음과 같이 구성되어 있다
- Composable 함수
- 실제 UI
- metadata
- 애니메이션 효과 등 화면에 대한 추가 정보를 제공
- Map<String, Any> 형태
entryProvider 함수를 만드는 방법은 두 가지다
1. 직접 만들기
entryProvider = { key ->
when (key) {
is NintendoList -> NavEntry(key) { Text("닌텐도 게임 목록") }
is NintendoDetail -> NavEntry(
key,
metadata = mapOf("gameDataKey" to "gameDataValue")
) { Text("게임 ${key.id}") }
else -> {
NavEntry(Unit) { Text(text = "잘못된 게임입니다: $it") }
}
}
}
이렇게 when 문을 활용하여 entryProvider 함수를 만들고, 각 키에 대한 브랜치를 쓴다
2. entryProvider DSL 사용
entryProvider = entryProvider {
entry<NintendoList> { Text("닌텐도 게임 목록") }
entry<NintendoDetail> (
metadata = mapOf("gameDataKey" to "gameDataValue")
) { key -> Text("게임 ${key.id}") }
}
DSL은 각 키 유형에 대해 테스트할 필요가 없으므로 람다 함수를 단순화하고, 각 키 유형의 NavEntry를 구성할 수 있다
이를 위해서 entryProvider 빌더 함수를 쓰며 키가 없는 경우의 기본 대체 동작을 포함하고 있다
- entry는 지정된 유형 및 컴포저블 콘텐츠로 NavEntry를 정의한다
- entry는 metadata 매개변수로 NavEntry.metadata를 설정한다
정리

1. 네비게이션 이벤트로 변경 시작
키는 사용자의 이벤트에 응답하여 백 스택에 푸시, 또는 팝된다
- 버튼을 클릭하면 MashUp(memberId = “아린”) 생성
- 백스택 [Home, MashUp(memberId = “아린”)
2. 백 스택 상태 변경으로 콘텐츠 탐색 트리거
NavDisplay는 백 스택을 계속 관찰하며 단일 창 레이아웃에서 최상위 백 스택 항목을 표시한다
백 스택 최상위 키가 변경되면, NavDisplay는 이 키를 entryProvider에 전달한다
3. entryProvider에서 콘텐츠 제공
entryProvider는 키를 NavEntry로 확인한다.
NavDisplay에서 키를 수신하면, 해당 key에 매칭되는 컴포저블 화면을 NavEntry로 제공한다
4. 콘텐츠 표시
NavDisplay가 NavEntry를 수신하게 되고, 해당 콘텐츠를 UI에 표시한다
https://android-developers.googleblog.com/2025/05/announcing-jetpack-navigation-3-for-compose.html
https://developer.android.com/guide/navigation/navigation-3
안드로이드 공식문서를 바탕으로 정리한 글이며, 잘못된 내용이 있다면 피드백 부탁드립니다
'개발일지 > Android' 카테고리의 다른 글
| [Android] 클린 아키텍처 VS 구글 권장 아키텍처, MVVM 패턴 (3) | 2024.04.19 |
|---|---|
| [Android] Jetpack Compose ViewModel 추가 (0) | 2023.08.07 |
| [Android] Compose - 이미지, 구분선, 텍스트, Preview, Button, 툴바 (0) | 2023.07.30 |
| [Android] 클린 아키텍처 (Clean Architecture) (0) | 2023.07.18 |
| [Android] Retrofit2 활용하여 API 연동 실전 예제 (0) | 2023.07.17 |