# Navigation 적용 가이드 > **기본 네비게이션** : push, pop 과 같이 1 depth 이동을 뜻합니다. > **커스텀 네비게이션** : popToRoot, 또는 특정 화면으로의 복잡한 이동 같은 네비게이션 흐름을 뜻합니다. ## 네비게이션 적용 ```swift func navigationDestination<V>( isPresented: Binding<Bool>, @ViewBuilder destination: () -> V ) -> some View where V : View ``` - 위 메서드를 이용하여 다음화면을 정의합니다. ### 기본 네비게이션 ```swift struct LaunchScreen { @State private var isPresentRealNameCheckView: Bool = false var body: some View { VStack { Text("A 화면") Button { isPresentRealNameCheckView = true } label: { Text("실명인증 버튼") } } .navigationDestination(isPresented: $isPresentRealNameCheckView) { RealNameCheckView( viewModel: .init(isJoin: false, publicKey: publicKey, consent: consent), isPresentSheet: $isPresentRealNameCheckView) } } } ``` ### 기본 네비게이션(커스텀 네비게이션 과정에 포함되는 화면일 경우) ```swift struct LaunchScreen { @Environment(\.injected) private var injected: DIContainer //*1 @State private var routingState: Routing = .init() //*2 private var routingBinding: Binding<Routing> { //*3 $routingState.dispatched(to: injected.appState, \.routing.launchScreen) } var body: some View { VStack { Text("어떤화면") Button { routingBinding.toRealNameCheckView.wrappedValue = true } label: { Text("실명인증 버튼") } } .navigationDestination(isPresented: routingBinding.toRealNameCheckView) { RealNameCheckView( viewModel: .init(isJoin: false, publicKey: publicKey, consent: consent), isPresentSheet: $isPresentRealNameCheckView ) .inject(injected) //*7 } .onReceive(routingUpdate) { //*4 routingState = $0 } } } extension LaunchScreen { struct Routing: Equatable { //*5 var toRealNameCheckView = false } } extension LaunchScreen { var routingUpdate: AnyPublisher<Routing, Never> { //*6 injected.appState.updates(for: \.routing.launchScreen) } } ``` <details> <summary> 별표(*)한 부분에 대한 세부 내용 </summary> - `*1 injected` : 외부에서 주입된 의존성 입니다. 외부에서부터 전달되었기 때문에 이전 화면들 에서의 상태값을 모두 가지고 있습니다. - `*2 routingState` : `LaunchScreen` 의 `Routing` 객체를 @State 변수로 선언합니다. - `*3 routingBinding` : `routingState`의 변화를 주입된 의존성에 전달 후 업데이트 하는 Binding 변수입니다. - `*6 routingUpdate` : `Routing` 타입을 발행하는 Publisher 타입입니다. 뷰에서 `.onReceive()` 메서드를 통해 값이 발행될 때 액션을 정의해줄 수 있습니다. - `*4 .onReceive()` : `Routing` 타입의 값이 발행되면 `routingState`에 해당 값을 할당해줍니다. 그럼 `routingState` 를 사용하는 `LaunchScreen` 내부의 모든 View타입에 업데이트가 일어납니다. - `*5 Routing 객체` - 각 뷰는 자신이 가지는 화면전환 정보를 Routing 객체로 관리할 수 있습니다. - 아래는 AppState에서 관리하는 정보들을 중첩타입으로 선언한 내용입니다. ```swift struct AppState: Equatable { var routing = ViewRouting() var auth = Auth() } extension AppState { struct ViewRouting: Equatable { // MARK: Entries var launchScreen = LaunchScreen.Routing() var initView = InitView.Routing() var homeView = HomeView.Routing() // MARK: Portfolio-Tab var portfolioTab = PortfolioTabView.Routing() var portfolioDetailView = PortfolioDetailView.Routing() var purchaseAgreeView = Portfolio_PurchaseAgreeView.Routing() var purchaseStep1NewView = Portfolio_PurchaseStep1NewView.Routing() // MARK: Magazine-Tab var magazineTab = MagazineTabView.Routing() // MARK: Wallet-Tab var walletTab = WalletTabView.Routing() // MARK: Setting-Tab var settingTab = SettingTabView.Routing() } struct Auth: Equatable { var shouldLogout = false } } ``` - 현재는 routing 정보와 auth 정보를 관리하고 있습니다. - routing 정보 내부에 각 뷰의 `Routing` 객체를 변수로 가지고 있습니다. - 각각의 뷰에 keyPath로 접근하여 값을 업데이트 시켜줄 수 있습니다. - 지금처럼 내부적으로 keyPath로 접근하도록 만들어놓은 Store 타입을 사용하면, `bulkUpdate()` 메서드를 이용해 간편하게 값을 업데이트 시켜줄 수 있습니다. - 업데이트 된 값은 *4(`.onReceive()`) 메서드가 걸려있는 곳으로 전해져 해당 뷰에 영향을 미칠 수 있습니다. - 위처럼 특정 View의 화면전환 정보를 Routing 객체로 분리하게 되면, 어느 뷰에서나 이 정보에 접근하여 수정을 할 수 있는 상태가 됩니다. - 즉, 어디서나 수정되길 원하는 화면전환 정보는 Routing 객체로 옮기고, 나머지는 기존처럼 @State private var isPresent... 형식으로 사용해도 무방합니다. </details> <br> ### 커스텀 네비게이션 ``` 위 화면에서 "실명인증 버튼" 을 눌러 RealNameCheckView로 이동하고, 또 비슷한 방식으로 그 하위뷰로 이동을 여러번 해서 `LastView` 까지 이동했다고 가정해봅니다. 다시 실명인증 버튼이 있는 `LaunchScreen` 화면으로 돌아오기 위해선 어떻게 해야할까요? `LaunchScreen.Routing` 이 가지는 화면전환 정보인 `toRealNameCheckView` 를 `false`로 만들어주면 됩니다 ``` ```swift struct LastView { @Environment(\.injected) private var injected: DIContainer var body: some View { VStack { Text("마지막 화면") Button { injected.bulkUpdate { $0.routing.launchScreen.toRealNameCheckView = false } } label: { Text("처음으로 돌아가기 버튼") } } } } ``` - 위 처럼 명령을 해주기 위해 전제되어야 할 조건은, `@Environment(\.injected) private var injected` 이 상태값이 이전 화면들의 상태값을 가지고 있어야합니다. - 그러려면 navigation 이동 시, DestinationView.inject(injected) 와 같이 의존성을 넘겨주어야 합니다. *7 처럼 처리해주면 됩니다.