
모바일 앱에서 라우팅은 단순히 “화면을 바꾸는 기능”이 아니다.
사용자가 앱 안에서 어디로 이동하고, 어떤 흐름으로 작업을 이어가며, 외부 링크를 통해 어떤 화면으로 진입하는지까지 포함하는 전체 구조에 가깝다.
React Native에서 이 구조를 어떻게 잡느냐에 따라 코드의 복잡도와 사용자 경험이 함께 달라진다.
이번 글에서는 Expo Router와 Deep Link를 중심으로 라우팅을 정리하고, 마지막에는 앱 설계에서 자주 쓰는 Navigation Pattern까지 함께 정리해보려 한다.
모바일 앱에서 라우팅은 사용자가 어떤 화면으로 이동할지 결정하는 흐름 관리다.
홈에서 상세 화면으로 들어가고, 상세 화면에서 다시 뒤로 돌아오고, 특정 링크를 눌렀을 때 앱이 바로 원하는 화면을 여는 것까지 모두 라우팅의 범주에 들어간다.
React Native에서는 보통 Navigation 라이브러리를 통해 이런 흐름을 구성한다.
대표적으로 Stack, Tab, Drawer 같은 navigator를 조합해 앱의 화면 구조를 설계하는 방식이 널리 쓰인다.
전통적인 React Native 앱에서는 라우팅 구조를 코드로 직접 정의하는 경우가 많았다.
예를 들어 어떤 화면이 Stack에 속하는지, 어떤 화면들이 Tab 안에 들어가는지, Drawer에 어떤 메뉴를 넣을지를 개발자가 navigator 코드 안에서 하나씩 선언하는 방식이다.
이 방식의 장점은 유연성이다.
화면 전환 규칙을 세밀하게 제어할 수 있고, 복잡한 구조도 직접 설계할 수 있다.
다만 앱 규모가 커질수록 화면 트리와 네비게이션 설정이 분산되기 쉬워지고, “현재 이 화면이 어떤 경로에 매달려 있는지”를 파악하는 비용도 커진다.
결국 라우팅이 복잡해질수록, 화면 자체보다 구조를 읽고 유지하는 일이 더 어려워지는 경우가 많다.
Expo Router는 이 문제를 파일 기반 라우팅으로 풀어낸다.
핵심은 단순하다.
파일 구조 자체를 라우팅 구조로 사용한다.
즉, 화면 경로를 코드 안에서 일일이 선언하는 대신, 라우트가 될 파일을 디렉터리에 배치하면 그 파일이 곧 화면이 된다.
이 방식은 웹 프레임워크에서 익숙한 파일 시스템 라우팅 개념을 React Native와 Expo 환경으로 가져온 것이다.
Expo Router가 흥미로운 이유는 새로운 라우팅 시스템처럼 보이지만, 내부적으로는 React Navigation 위에서 동작한다는 점이다.
즉, 기존 생태계를 버리는 것이 아니라, 그 위에 더 구조화된 방식의 인터페이스를 얹는 셈이다.
파일 기반 라우팅에서는 파일 경로와 URL 경로가 자연스럽게 연결된다.
예를 들어 다음과 같은 구조가 있다고 하자.
app/
├── index.tsx
└── profile/
└── [id].tsx
이 구조는 보통 다음처럼 이해할 수 있다.
/ → index.tsx/profile/1 → profile/[id].tsx여기서 중요한 건 “라우트 매핑을 별도 코드로 반복 선언할 필요가 크게 줄어든다”는 점이다.
파일을 만들면 페이지가 되고, 폴더를 나누면 경로가 된다.
물론 모든 네비게이션 코드가 완전히 사라지는 것은 아니다.
실제 앱에서는 _layout.tsx를 통해 Stack, Tabs 같은 레이아웃을 정의하고, 필요한 경우 초기 라우트나 공통 레이아웃을 구성한다.
하지만 적어도 “이 화면의 경로가 무엇인지”는 파일 구조만 봐도 훨씬 직관적으로 이해할 수 있다.
Expo Router를 이해할 때 중요한 특징은 네 가지다.
첫째, 파일이 곧 라우트다.
화면을 추가하는 행위와 라우트를 추가하는 행위가 거의 같은 일이 된다.
둘째, 모든 페이지가 URL을 가진다.
이 점이 단순히 편한 수준을 넘어서 중요한 이유는, 웹에서는 주소가 되고 모바일에서는 딥링크 경로가 되기 때문이다.
셋째, Deep Link와의 궁합이 좋다.
URL과 화면 구조가 자연스럽게 대응되기 때문에, 외부에서 특정 화면으로 들어오는 경로를 설계하기가 쉬워진다.
넷째, 기존 React Navigation 기반과 단절되지 않는다.
완전히 새로운 철학처럼 보이지만, 실제로는 React Navigation의 기능을 더 구조적으로 쓰게 도와주는 방식에 가깝다.
라우팅은 앱 내부 이동만 의미하지 않는다.
외부에서 앱 내부 특정 화면으로 바로 들어오는 흐름도 포함한다.
이때 사용하는 것이 Deep Link다.
Deep Link는 특정 URL을 통해 앱 내부의 특정 화면을 여는 방식이다.
예를 들어 아래처럼 생각할 수 있다.
myapp://profile/1
이 링크를 누르면 앱이 실행되면서 profile/1 화면으로 바로 진입한다.
즉, 사용자는 홈 화면을 거치지 않고 원하는 목적지로 곧바로 이동하게 된다.
이 기능은 회원 초대, 이벤트 페이지, 비밀번호 재설정, 푸시 알림 연결, 공유 링크 같은 흐름에서 특히 중요하다.
가장 기본적인 방식은 Custom Scheme이다.
myapp://profile/1
이 방식은 앱 고유의 스킴을 등록해서 사용하는 형태다.
앱이 설치되어 있으면 해당 링크가 앱으로 연결된다.
구현이 단순하고 테스트도 쉬운 편이지만, 링크 자체가 웹 주소와 자연스럽게 연결되지는 않는다.
또 사용자가 앱을 설치하지 않은 경우에는 웹 fallback 경험을 설계하기 어렵다.
좀 더 실무적인 방식은 https:// 기반 링크를 사용하는 것이다.
https://example.com/profile/1
이 방식은 iOS에서는 Universal Link, Android에서는 App Link라고 부른다.
앱이 설치되어 있으면 앱으로 열리고, 설치되어 있지 않으면 웹으로 연결할 수 있다.
즉, 사용자 경험 측면에서 더 자연스럽고 배포 환경에서도 유리하다.
다만 이 방식은 단순히 경로만 맞춘다고 끝나는 게 아니라, 도메인 검증과 플랫폼 설정 같은 추가 작업이 필요하다.
Expo Router는 모든 페이지가 URL을 가지는 구조이기 때문에 Deep Link와 아주 자연스럽게 이어진다.
예를 들어 다음 파일이 있다고 하자.
app/profile/[id].tsx
그러면 개념적으로는 다음 같은 링크와 바로 연결된다.
myapp://profile/1
또는 Universal Link를 쓴다면:
https://example.com/profile/1
이때 중요한 포인트가 하나 있다.
Expo Router는 라우트 매핑과 런타임 라우팅을 자동화해주기 때문에, Deep Link를 받을 때 별도의 JavaScript 라우팅 설정을 많이 줄여준다.
하지만 “아무 설정도 필요 없다”는 뜻은 아니다.
모바일 앱에서 실제로 딥링크를 받으려면 app.json 또는 앱 설정 파일에 scheme를 지정해야 하고, Universal Link/App Link를 쓰려면 도메인과 플랫폼 검증 작업도 필요하다.
정리하면 이렇다.
이 구분을 이해하면 Expo Router와 Deep Link를 훨씬 정확하게 볼 수 있다.
라우팅이 “경로”의 문제라면, Navigation Pattern은 “구조”의 문제다.
같은 앱이라도 Stack 중심으로 설계하느냐, Tabs 중심으로 설계하느냐, Drawer까지 쓰느냐에 따라 사용자의 탐색 경험은 크게 달라진다.
대표적인 패턴은 Stack, Tab, Drawer 세 가지다.
Stack Navigation은 화면이 위로 차곡차곡 쌓이는 구조다.
예를 들어 사용자가 홈에서 목록으로 가고, 목록에서 상세로 들어가면 새 화면이 이전 화면 위에 올라간다.
그래서 “뒤로 가기”라는 행동이 매우 자연스럽다.
단계적인 흐름이 있을 때 적합하다.
예를 들면:
즉, 이전 화면과 다음 화면이 부모-자식 관계처럼 이어질 때 Stack이 가장 자연스럽다.
Tab Navigation은 같은 레벨의 주요 화면들을 탭으로 나눠 놓는 구조다.
보통 앱 하단에 배치되며, 사용자는 탭을 눌러 주요 기능 사이를 빠르게 오갈 수 있다.
예를 들면:
이 구조의 핵심은 “병렬적인 주요 기능”이다.
즉, 어느 하나가 다른 하나의 하위 화면이 아니라, 서로 같은 레벨의 핵심 진입점일 때 적합하다.
Drawer Navigation은 화면 옆에서 슬라이드되어 나오는 메뉴 구조다.
화면을 깔끔하게 유지하면서도 많은 메뉴를 담을 수 있다는 장점이 있다.
설정, 관리 메뉴, 계정 전환, 보조 기능처럼 “항상 전면에 둘 필요는 없지만 접근은 가능해야 하는 메뉴”를 배치할 때 유용하다.
| 구분 | Stack | Tab | Drawer |
|---|---|---|---|
| 구조 | 계층형 | 병렬형 | 숨김 메뉴형 |
| 이동 방식 | 순차 이동 | 즉시 전환 | 메뉴 선택 |
| 화면 관계 | 부모-자식 | 동일 레벨 | 선택적 접근 |
| 적합한 상황 | 단계적 흐름 | 주요 기능 빠른 전환 | 많은 메뉴 정리 |
| 대표 예시 | 목록 → 상세 | 홈 / 검색 / 프로필 | 설정 / 관리자 메뉴 |
내비게이션 패턴은 “무엇이 더 좋다”의 문제가 아니라 “어떤 흐름에 더 맞는가”의 문제다.
Stack은 흐름을 만들기 좋지만, 구조가 깊어지면 탐색 부담이 커진다.
Tab은 접근성이 뛰어나지만, 핵심 기능 수가 늘어나면 금방 한계가 온다.
Drawer는 많은 메뉴를 담기 좋지만, 사용자가 메뉴의 존재를 잊기 쉽다.
그래서 실무에서는 한 가지 패턴만 쓰기보다 조합해서 쓰는 경우가 많다.
예를 들면 이런 식이다.
결국 중요한 것은 패턴 자체보다, 사용자가 “지금 어디에 있고 다음에 어디로 가는지”를 헷갈리지 않게 만드는 것이다.
Expo Router는 React Native 라우팅을 더 구조적으로 만들기 위한 도구라고 볼 수 있다.
파일 기반 라우팅을 통해 경로와 화면 구조를 더 직관적으로 연결하고, 모든 페이지가 URL을 가지는 구조 덕분에 Deep Link와도 잘 맞는다.
다만 이 점을 너무 단순하게 이해하면 안 된다.
Expo Router가 라우트 매핑을 자동화해주더라도, 실제 모바일 딥링크를 받기 위해서는 scheme, Universal Link/App Link, 도메인 검증 같은 설정이 필요하다.
그리고 라우팅을 잘한다는 건 단지 경로를 잘 만드는 것이 아니라, 어떤 Navigation Pattern을 선택할지까지 함께 설계하는 일이다.
정리하면 다음과 같다.