IT

SwiftUI Navigation + WebView 연동 완벽 가이드 (딥링크, 푸시, 라우팅까지)

초코모찌롤 2026. 4. 3. 10:03
반응형
SwiftUI Navigation + WebView 연동 완벽 가이드 (딥링크, 푸시, 라우팅까지)

SwiftUI Navigation + WebView 연동 완벽 가이드 (딥링크, 푸시, 라우팅까지)

실무에서 가장 복잡한 구조 중 하나는 Navigation + WebView + 딥링크 조합입니다.

특히 아래 상황에서 문제가 발생합니다.

  • 푸시 클릭 시 특정 WebView 화면 이동
  • 웹에서 특정 앱 화면으로 이동
  • NavigationStack과 WebView 상태 충돌

이 글에서는 이 구조를 안정적으로 구현하는 방법을 정리합니다.


1. 전체 구조 이해

핵심 구조는 다음과 같습니다.

  • Router (Navigation 관리)
  • WebView (웹 화면)
  • DeepLink Handler

👉 모든 이동은 Router를 통해 처리


2. Route 정의


enum Route: Hashable {
    case home
    case web(url: URL)
    case detail(id: Int)
}

👉 모든 화면을 하나의 enum으로 관리


3. Router 구현


class Router: ObservableObject {
    @Published var path: [Route] = []

    func push(_ route: Route) {
        path.append(route)
    }

    func pop() {
        path.removeLast()
    }

    func reset(_ route: Route) {
        path = [route]
    }
}

👉 Navigation의 단일 진입점


4. NavigationStack 연결


NavigationStack(path: $router.path) {
    HomeView()
        .navigationDestination(for: Route.self) { route in
            switch route {
            case .home:
                HomeView()
            case .web(let url):
                WebView(url: url)
            case .detail(let id):
                DetailView(id: id)
            }
        }
}

5. 푸시 알림 → WebView 이동

푸시 데이터 예시:


{
    "type": "web",
    "url": "https://example.com"
}

처리 코드:


func handlePush(data: [String: Any]) {
    if let urlString = data["url"] as? String,
       let url = URL(string: urlString) {
        router.push(.web(url: url))
    }
}

👉 앱 어디서든 동일한 방식으로 처리


6. 딥링크 처리


func handleDeepLink(url: URL) {
    if url.path.contains("detail") {
        router.push(.detail(id: 1))
    }
}

👉 URL → Route 변환


7. Web → App 이동 (JS 브릿지)

웹에서 메시지 전달:


window.webkit.messageHandlers.callback.postMessage({
    type: "detail",
    id: 10
});

앱에서 처리:


func userContentController(_ userContentController: WKUserContentController,
                           didReceive message: WKScriptMessage) {

    if let data = message.body as? [String: Any],
       let type = data["type"] as? String {

        if type == "detail" {
            router.push(.detail(id: 10))
        }
    }
}

👉 Web → Native 라우팅 연결


8. 상태 충돌 방지

자주 발생하는 문제:

  • 중복 push
  • NavigationStack 꼬임
  • WebView reload 문제

해결 전략

  • Route 중복 체크
  • Router 단일화
  • WebView 상태 분리

9. 실무 추천 구조

  • 모든 이동 → Router
  • WebView는 화면 역할만 수행
  • 딥링크 / 푸시 → Route 변환 후 처리

10. 전체 흐름 정리

  1. 푸시 / 딥링크 수신
  2. Route 변환
  3. Router.push()
  4. NavigationStack 반영

11. 결론

Navigation + WebView의 핵심은 다음입니다.

모든 이동을 "데이터(Route)"로 통제한다

  • 푸시 → Route
  • 딥링크 → Route
  • 웹 이벤트 → Route

이 구조를 만들면 복잡한 앱에서도 안정적인 네비게이션이 가능합니다.


다음 글에서는 SwiftUI 성능 문제 실전 케이스 (List, Navigation, WebView)를 다루면 전체 구조가 완성됩니다.

반응형