NavigationStack은 iOS 16+에서 도입되어 NavigationView를 대체한 SwiftUI의 최신 네비게이션 컨테이너입니다. Apple Developer Documentation, 2024에 따르면, NavigationStack은 타입 안전한 네비게이션 경로(NavigationPath)를 통해 화면 스택을 관리하며, 딥 네비게이션, 루트 화면으로의 프로그래매틱 복귀, 데이터 변경 시 상태 보존을 지원합니다. NavigationView와 달리 NavigationStack은 추가 컨테이너로 래핑할 필요가 없으며 네비게이션 경로에 직접 Binding을 제공합니다.
핵심 포인트
NavigationStack은 스택 기반 네비게이션(LIFO)을 구현하는 컨테이너 View입니다. 전환 기록을 관리하여 새 화면을 스택에 푸시하고 시스템 “뒤로” 버튼이나 프로그래매틱 방식으로 돌아갈 수 있습니다. NavigationStack은 iOS 16, iPadOS 16, macOS 13, watchOS 9, tvOS 16부터 SwiftUI의 일부입니다.
NavigationStack의 주요 혁신은 타입 안전 네비게이션 경로입니다. NavigationLink를 만들 때 직접 대상을 지정하는 대신 경로에 값을 넣고 대상 View는 .navigationDestination(for:destination:) 수정자를 통해 별도로 등록합니다. 이렇게 하면 네비게이션과 렌더링이 분리되어 코드가 더 모듈화되고 테스트 가능해집니다.
WWDC 2022 (세션 10054)에 따르면, NavigationStack은 ObservableObject와 SwiftUI 라이프사이클을 기반으로 한 새로운 네비게이션 메커니즘을 사용합니다. 내부적으로 UINavigationController에 의존했던 NavigationView와 달리 NavigationStack은 완전히 SwiftUI로 구현되어 SwiftUI 라이프사이클과의 예측 가능성 및 호환성이 향상되었습니다.
NavigationStack은 루트 View와 선택적 네비게이션 경로(NavigationPath 또는 Hashable 값 배열에 대한 Binding)를 받습니다. 모든 자식 화면은 NavigationLink를 통해 또는 경로에 프로그래매틱 방식으로 값을 추가하여 스택에 푸시됩니다.
NavigationView는 iOS 16 이전 SwiftUI의 주요 네비게이션 컨테이너였습니다. 내부적으로 UINavigationController를 자동으로 관리했으며, 이로 인해 데이터 변경 시 예측 불가능한 동작, 프로그래매틱 네비게이션의 복잡성, 타입 안전성 부족 등의 여러 문제가 발생했습니다.
| 특성 | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| 네비게이션 유형 | 스택 (LIFO) | 스택 (LIFO) |
| 네비게이션 경로 | 타입 지정됨 (NavigationPath) | 지원 안 함 |
| Deep linking | 내장 지원 | 해결 방법 필요 |
| 프로그래매틱 복귀 | 경로 통해 (pop, popToRoot) | dismiss, presentationMode |
| 내부 구현 | SwiftUI 네이티브 | UINavigationController |
| 호환성 | iOS 16+ | iOS 13+ |
주요 장점 NavigationStack의 — 타입 안전 네비게이션입니다. 경로를 특정 유형의 배열(또는 이종 스택의 경우 NavigationPath)로 정의하고 각 유형에 대상을 등록합니다. 이렇게 하면 유형 불일치 오류가 제거되고 네비게이션이 예측 가능해집니다.
NavigationView는 iOS 17에서 더 이상 사용되지 않습니다. Apple은 모든 새 프로젝트와 iOS 16으로 최소 버전을 업데이트할 때 NavigationStack으로 마이그레이션할 것을 권장합니다.
NavigationPath는 NavigationStack의 네비게이션 경로를 나타내는 유형입니다. 이종 값(AnyHashable)을 저장하거나 배열 [T: Hashable]에 대한 Binding을 통해 특정 유형과 함께 사용할 수 있습니다. NavigationPath는 상태 보존을 위해 자동으로 인코딩 및 디코딩됩니다.
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
이종 스택: NavigationPath는 Hashable을 구현하는 경우 다른 유형의 값을 포함할 수 있습니다. 예를 들어, 첫 번째 화면은 String(ID)을, 두 번째는 Int(숫자)를, 세 번째는 사용자 정의 enum Route를 받을 수 있습니다. 각 유형은 표시를 위해 별도의 .navigationDestination을 등록합니다.
Codable 지원: NavigationPath는 경로의 모든 값이 Codable + Hashable인 경우 Codable을 구현합니다. 이를 통해 앱을 다시 시작하거나 백그라운드로 전환할 때 네비게이션 상태를 저장하고 복원할 수 있습니다.
.navigationDestination(for:destination:) — 특정 데이터 유형에 대한 대상 View를 등록하는 수정자입니다. NavigationLink가 이 유형의 값을 경로에 넣으면 SwiftUI가 자동으로 해당 .navigationDestination을 찾아 화면을 만듭니다.
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
중요 규칙: .navigationDestination은 NavigationStack 내부에 있는 View에, 그리고 NavigationLink가 경로에 값을 넣기 전에 적용되어야 합니다. 일반적으로 루트 View나 섹션 컨테이너에 추가됩니다. 값 유형에 대한 .navigationDestination을 찾을 수 없으면 전환이 발생하지 않습니다.
SwiftUI Engineering (2023)에 따르면, .navigationDestination은 계층 구조의 다양한 수준에서 등록될 수 있습니다. SwiftUI는 네비게이션 중에 가장 가까운 일치하는 대상을 찾습니다. 이를 통해 애플리케이션의 다른 부분에서 한 유형에 대한 대상을 재정의할 수 있습니다.
패턴 1: enum Route를 통한 네비게이션. 애플리케이션의 모든 화면에 대해 연관 값이 있는 enum을 정의합니다. AppRoute에 하나의 .navigationDestination과 라우팅용 switch를 사용합니다. 이렇게 하면 애플리케이션의 모든 가능한 전환에 대한 단일 정보 소스가 제공됩니다.
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
패턴 2: 프로그래매틱 네비게이션 및 deep linking. NavigationStack은 프로그래매틱 스택 관리(화면 추가, 제거, 루트로 복귀)를 가능하게 합니다. 푸시 알림, 딥 링크 및 재시작 후 네비게이션 복원에 필요합니다.
패턴 3: NavigationPath 대신 유형 배열 사용. 모든 화면이 동일한 유형(예: String 또는 사용자 정의 enum)을 사용하는 경우 [T]에 대한 Binding을 사용합니다. 이렇게 하면 NavigationPath보다 더 강력한 타입 지정과 더 나은 성능을 제공합니다. NavigationPath는 다른 화면 유형이 있는 이종 스택에 적합합니다.
Point-Free (2024)에 따르면, enum Route와 함께 NavigationStack을 사용하는 것이 SwiftUI 애플리케이션에서 네비게이션을 구성하는 선호되는 방법입니다. 모든 가능한 전환을 명시적, 타입 안전, 테스트 가능하게 만들어 수십 개의 화면이 있는 대규모 프로젝트에서 특히 중요합니다.
자주 묻는 질문
NavigationStack — 타입 안전 경로를 통해 화면 스택을 관리하는 SwiftUI 네비게이션 컨테이너(iOS 16+)입니다. NavigationView를 대체하며 deep linking, 프로그래매틱 네비게이션, 상태 보존을 지원합니다.
NavigationStack은 NavigationLink를 대상에 직접 바인딩하는 대신 타입 안전 경로(NavigationPath)를 사용합니다. 프로그래매틱 네비게이션, deep linking 및 상태 보존을 위한 Codable을 지원합니다. UINavigationController가 아닌 SwiftUI에서 작동합니다.
NavigationPath는 스택의 화면 시퀀스를 나타내는 유형입니다. path.append() 또는 value가 있는 NavigationLink를 통해 경로에 값을 추가합니다. 각 유형은 표시를 위해 .navigationDestination을 등록합니다. NavigationPath는 Codable 및 자동 상태 보존을 지원합니다.
프로그래매틱 경로 관리를 통해: URL을 처리한 후 해당 경로 값으로 path.append()를 호출합니다. NavigationStack이 자동으로 대상 화면을 표시합니다. 루트로 복귀 — path.removeLast(path.count).
네, 최소 버전이 iOS 16+인 경우입니다. NavigationView는 iOS 17에서 더 이상 사용되지 않습니다. 마이그레이션은 타입 안전 네비게이션, deep linking 지원, SwiftUI 라이프사이클과의 더 나은 정렬을 제공합니다. iOS 15 이하를 대상으로 하는 프로젝트는 당분간 NavigationView를 계속 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.