Router — iOS 및 Android 라우팅 패턴 기초

저자: IT Sectr 게시일: 2026-02-19 읽는 시간: 9 분

Router(라우터) — 애플리케이션 화면 간의 내비게이션 로직을 중앙화하는 아키텍처 패턴입니다. Router는 이벤트 발생 시 어디로 어떻게 이동할지 결정합니다. iOS에서 Router는 UINavigationController와 segue를 추상화하고, Android에서는 FragmentManager와 Jetpack Navigation의 NavController를 추상화합니다. Router는 독립적인 패턴으로 또는 Coordinator(Router-per-Coordinator)와 VIPER(Router-per-module)의 구성 요소로 작동할 수 있습니다. 자세한 내용은 Android Navigation Component Guide를 참조하세요.

핵심 포인트

  • Router — 화면 간 내비게이션을 중앙화하는 라우팅 패턴
  • 두 가지 수준 — Router는 Coordinator의 구성 요소 또는 독립 패턴이 될 수 있음
  • iOS Router — push/present/pop 메서드가 있는 UINavigationController 추상화
  • Android Router — NavController 및 deep links를 갖춘 Jetpack Navigation Component
  • URL Routing — URL 스키마(deeplink, universal link) 기반 라우팅

Router란: 라우팅 패턴의 본질

Router — 화면 간 전환 로직을 캡슐화하는 패턴입니다. 주요 개념: ViewController(또는 Presenter/ViewModel)는 직접 navigationController.pushViewController를 호출하지 않고 Router에 탐색 의도를 알립니다. Router는 어떤 ViewController를 생성하고 어떻게 표시할지 결정합니다. Router는 단순(내비게이션 메서드 집합)하거나 복잡(URL 스키마 라우팅, deep links 및 푸시 알림 포함)할 수 있습니다.

Router의 세 가지 접근 방식 — 서비스로서의 Router(전역 Navigator 싱글톤), 모듈 구성 요소로서의 Router(VIPER 접근 방식), 프로토콜로서의 Router(Coordinator 추상화). 전역 Router — 단순성(앱당 하나의 인스턴스) — 소규모 프로젝트에서 인기 있음. 모듈식 Router — 각 기능에 자체 Router — VIPER 및 Clean Architecture 표준. 프로토콜 Router — Coordinator에서 사용: push/present/pop/dismiss 메서드가 있는 RouterProtocol.

접근 방식구조사용 시기
전역 Navigator내비게이션 메서드가 있는 싱글톤소규모 프로젝트, 프로토타입
모듈식 Router각 모듈 내부의 RouterVIPER, Clean Architecture
프로토콜 RouterCoordinator용 프로토콜 + 구현MVVM-C, Coordinator 패턴

패턴의 역사 — Router는 웹 프레임워크(URL Router — Rails Routes, Express.js)에서 등장하여 모바일 개발에 적용되었습니다. iOS에서는 VIPER(2015-2017)의 부상과 함께 Router가 대중화되었습니다. Android에서는 Router가 Jetpack Navigation(2018)의 일부이며, Google의 표준 내비게이션 솔루션입니다. 오늘날 Router는 모든 모듈식 모바일 애플리케이션 아키텍처의 필수적인 부분입니다.

iOS의 Router: Swift 구현

iOS Router — 다양한 내비게이션 유형에 대한 메서드가 있는 프로토콜입니다. 기본 구현은 UINavigationController를 래핑합니다. Router는 모달 프레젠테이션(present/dismiss), 내비게이션 스택의 push/pop, Split View Controller의 showDetail 및 전체 화면 프레젠테이션(iOS 13+)을 처리할 수 있습니다. Router는 ViewController를 생성하지 않으며 준비된 인스턴스를 받아서 표시합니다. ViewController 생성은 팩토리(Assembly, DI 컨테이너) 또는 Coordinator가 처리합니다.

swift
// iOS용 RouterProtocol
protocol RouterProtocol {
    func push(_ viewController: UIViewController, animated: Bool)
    func pop(animated: Bool)
    func popToRoot(animated: Bool)
    func present(_ viewController: UIViewController, animated: Bool)
    func dismiss(animated: Bool)
    func setViewControllers(_: [UIViewController], animated: Bool)
}

// UINavigationController를 통한 Router 구현
class NavigationRouter: RouterProtocol {
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func push(_ vc: UIViewController, animated: Bool) {
        if navigationController.presentedViewController {
            navigationController.dismiss(animated: false)
        }
        navigationController.pushViewController(vc, animated: animated)
    }

    func pop(animated: Bool) {
        navigationController.popViewController(animated: animated)
    }

    func popToRoot(animated: Bool) {
        navigationController.popToRootViewController(animated: animated)
    }

    func present(_ vc: UIViewController, animated: Bool) {
        navigationController.present(vc, animated: animated)
    }

    func dismiss(animated: Bool) {
        navigationController.dismiss(animated: animated)
    }

    func setViewControllers(_ vcs: [UIViewController], animated: Bool) {
        navigationController.setViewControllers(vcs, animated: animated)
    }
}

// Assembly — Router로 모듈 생성
protocol ProfileModuleFactory {
    func makeProfileView() -> UIViewController
}

VIPER의 Router — 각 VIPER 모듈에는 현재 모듈에서 어떤 화면을 열지 알고 있는 자체 Router가 있습니다. Router는 팩토리를 통해 다음 모듈의 ViewController를 생성하고 제어권을 전달합니다. VIPER의 Router는 import UIKit을 포함하는 유일한 구성 요소입니다(View 제외). 이렇게 하면 Presenter와 Interactor의 테스트가 간소화됩니다: UIKit에 의존하지 않습니다.

Android의 Router: Jetpack Navigation Component

Jetpack Navigation Component — 2018년부터 Google이 권장하는 Android 표준 라우터입니다. 내비게이션은 XML 그래프(nav_graph.xml)로 정의됩니다: 노드(fragments, activities, destinations)와 가장자리(actions). NavController는 전환을 관리합니다: FragmentManager.replace/add를 호출하고 백 스택을 관리합니다. Navigation Component는 deep links, 인수, 전환 애니메이션 및 type-safe 데이터 전달을 위한 Safe Args를 지원합니다.

kotlin
// nav_graph.xml — 내비게이션 그래프
<!-- @xml/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/loginFragment">

    <fragment android:id="@+id/loginFragment"
        android:name=".ui.login.LoginFragment">
        <action android:id="@+id/to_home"
            app:destination="@id/homeFragment" />
    </fragment>

    <fragment android:id="@+id/homeFragment"
        android:name=".ui.home.HomeFragment">
        <argument android:name="userId"
            android:defaultValue="0"
            app:argType="integer" />
        <deepLink app:uri="myapp://home/{userId}" />
    </fragment>
</navigation>

// Kotlin — NavController를 통한 내비게이션
class LoginFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.loginButton.setOnClickListener {
            val action = LoginFragmentDirections.toHome(userId = 42)
            findNavController().navigate(action)
        }
    }
}

// Safe Args — 타입 안전 인수
class HomeFragmentArgs : NavArgs {
    val userId: Int get() = arguments?.getInt("userId") ?: 0
}

// NavigationUI — Toolbar, BottomNav, Drawer와 통합
class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val navHost = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHost.navController
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}

Android Navigation의 Deep links — Navigation Component는 명시적 deep links(PendingIntent + NavDeepLinkBuilder)와 암시적 deep links(매니페스트의 intent-filter를 통해)를 지원합니다. deep link는 nav_graph의 모든 노드로 연결될 수 있습니다. 푸시 알림 처리: Notification이 NavDeepLinkBuilder로 PendingIntent를 생성하여 백 스택을 복원하고 원하는 화면을 엽니다. Navigation Component는 Up/Back 버튼을 자동으로 처리하고 사용자를 이전 화면으로 되돌립니다.

URL Routing: Deeplink 및 Universal Link 라우팅

URL Routing — URL 스키마, deep links 및 universal links 기반 라우팅입니다. 앱은 스키마(myapp://profile/42) 또는 universal link(https://example.com/profile/42)를 등록합니다. Router는 URL을 구문 분석하고 매개변수(userId=42)를 추출한 다음 해당 화면을 엽니다. URL Routing을 통해 앱을 외부에서 열 수 있습니다: 웹사이트 링크, 이메일, 푸시 알림, QR 코드를 통해. iOS는 NSUserActivity + universal link를 사용하고, Android는 intent-filter + deep link를 사용합니다.

swift
// URL Router — deep link 파싱 및 내비게이션
protocol URLRoute {
    var pattern: String { get }
    func navigate(parameters: [String: String], router: RouterProtocol)
}

struct ProfileRoute: URLRoute {
    let pattern = "myapp://profile/{userId}"

    func navigate(parameters: [String: String], router: RouterProtocol) {
        guard let userId = parameters["userId"] else { return }
        let profileVC = ProfileViewController(userId: userId)
        router.push(profileVC, animated: true)
    }
}

// URL Router — URL을 경로에 매핑
class URLRouter {
    private var routes: [URLRoute] = []

    func handle(_ url: URL, router: RouterProtocol) {
        for route in routes {
            if let params = matchPattern(route.pattern, url: url) {
                route.navigate(parameters: params, router: router)
                return
            }
        }
    }
}

실제 Deep link 라우팅 — 앱은 세 가지 상태에서 deep links를 처리해야 합니다: 실행 중이 아님(deep link로 시작), 백그라운드(백그라운드에서 복원), 활성(onNewIntent/SceneDelegate). URL Router는 백 스택을 올바르게 복원해야 합니다: 프로필 화면으로 deep link할 때 사용자가 Back을 눌러 이전 화면으로 돌아갈 수 있어야 합니다. iOS와 Android는 백 스택을 다르게 처리하므로 Router는 플랫폼별 특성을 고려해야 합니다.

Router와 Coordinator 및 Navigator 비교

Router vs Coordinator — Router는 "화면을 어떻게 표시할까?"(push/present)라는 질문에 답하고, Coordinator는 "어떤 화면을 표시할까?"(어떤 플로우를 시작할지)에 답합니다. Router는 내비게이션 도구이고 Coordinator는 플로우 구성자입니다. Coordinator는 Router를 사용합니다. VIPER에서 Router는 두 기능을 모두 수행합니다: 어디로 갈지와 어떻게 표시할지 결정합니다. MVVM-C에서 Router는 Coordinator가 사용하는 별도의 프로토콜입니다. 패턴들은 상호 배타적이지 않고 상호 보완적입니다.

특성RouterCoordinatorNavigator
초점내비게이션 메커니즘내비게이션 플로우전역 접근
수준UI 프레임워크(push/present)비즈니스 로직(플로우)인프라
화면 생성준비된 VC 수신팩토리를 통해 VC 생성VC 생성
수명 주기내비게이션당 하나childCoordinatorsSingleton

Router vs Navigator — Navigator는 iOS 커뮤니티에서 Router의 이전 이름입니다. 초기 구현에서 Navigator는 내비게이션 메서드가 있는 Singleton 클래스였습니다. Navigator는 전역 Router 싱글톤입니다. Navigator는 더 간단하지만 숨겨진 종속성을 만듭니다. 프로토콜 기반 Router는 더 테스트 가능하고 모듈식입니다. Navigator는 엄격한 아키텍처가 없는 프로젝트에 적합하고, Router는 Clean Architecture 및 VIPER에 적합합니다.

자주 묻는 질문

Router와 Coordinator의 차이점은 무엇인가요?

Router는 UINavigationController(push/present/pop)의 추상화입니다. Coordinator는 Router 위의 계층으로 플로우(다음에 표시할 화면)를 관리합니다. Router는 메커니즘을 처리하고 Coordinator는 내비게이션 로직을 처리합니다. Coordinator는 전환을 수행하기 위해 Router를 사용합니다. VIPER에서 Router는 두 기능을 모두 결합하고 MVVM-C에서는 분리됩니다.

모바일 앱에서 Router를 반드시 사용해야 하나요?

아니요. 3-5개 화면의 앱에서는 navigationController.pushViewController를 직접 호출해도 충분합니다. Router는 10개 이상의 화면, deep links, 푸시 알림 및 모듈식 아키텍처에서 유용해집니다. Router는 내비게이션 테스트를 단순화하고 모든 소스(버튼, deep link, 푸시 알림)에서 전환을 중앙에서 처리할 수 있게 합니다.

Router와 Deep Links는 어떤 관계인가요?

URL Router는 deep links와 universal links를 구문 분석하는 Router의 확장입니다. URL Router는 URL과 앱 화면 간의 매핑을 결정합니다. 예: 경로 myapp://profile/42 → ProfileViewController(userId: 42). URL Router는 다양한 소스(푸시 알림, QR 코드, 이메일 링크, 유니버설 링크)의 deep link 처리를 중앙화합니다.

Coordinator 없이 Router를 사용할 수 있나요?

네. Router는 독립적으로 작동할 수 있습니다 — 전역 Navigator로 또는 VIPER 모듈 구성 요소로. Coordinator 없는 Router는 단순히 프로토콜 뒤에 UINavigationController를 캡슐화하는 것입니다. Coordinator 없는 Router는 더 복잡하지만(ViewController가 push/present를 직접 호출) 가능합니다. 가장 좋은 방법은 Router + Coordinator를 함께 사용하는 것입니다.

Android의 Router는 iOS Router와 동일한가요?

기능적으로 — 네, 둘 다 내비게이션을 중앙화합니다. 구현은 다릅니다: iOS Router — UINavigationController를 래핑하는 프로토콜 + 클래스; Android — XML 그래프 및 NavController가 있는 Jetpack Navigation Component. Android Router는 프레임워크(Navigation Component)에 내장되어 있고, iOS Router는 타사 구현입니다. Android Router는 Safe Args를 지원하지만 iOS Router는 지원하지 않습니다(generics 또는 Codable 필요).

요약

  • Router — 화면 간 전환 로직을 캡슐화하는 라우팅 패턴
  • iOS Router — UINavigationController를 래핑하는 push/present/pop 메서드의 프로토콜
  • Android Router — XML nav_graph 및 NavController가 있는 Jetpack Navigation Component
  • URL Routing — deep links 및 universal links를 통한 라우팅
  • Router vs Coordinator — Router는 메커니즘, Coordinator는 내비게이션 플로우 처리
  • 적용 — 모듈식 아키텍처, deep links, 내비게이션 테스트

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기