[rn-expo] Link와 useRouter는 결국 같은 구현체였다
Expo Router를 쓰다 보면 자연스럽게 Link 컴포넌트와 useRouter() 훅을 자주 보게 된다.
겉보기엔 완전히 다른 방식처럼 보이지만, 실제 내부 구현을 따라가 보면 둘이 결국 같은 로직을 공유하고 있다는 사실을 확인할 수 있다.
이 글에서는 그 흐름을 코드 레벨에서 직접 따라가 보면서 정리해보려고 한다.
1. Link 컴포넌트의 내부로 들어가보자
먼저 출발점은 Link 컴포넌트.
👉 코드 위치
expo-router/link/Link.tsx — 80번째 줄
https://github.com/expo/router/blob/main/packages/expo-router/src/link/Link.tsx#L80
여기서 중요한 부분은 useLinkToPathProps라는 훅을 호출한다는 점이다:

그럼 자연스럽게 이 훅 내부로 들어가게 된다.
2. useLinkToPathProps 훅
👉 코드 위치
useLinkToPathProps.tsx — 35번째 줄
https://github.com/expo/router/blob/main/packages/expo-router/src/link/useLinkToPathProps.tsx#L35
여기서 링크 클릭 시 어떤 동작을 할지 결정해주는 핵심 함수인 linkTo가 등장한다.

linkTo의 역할은 단순하다:
- 특정 경로로 이동한다
- push, replace 등 navigation 액션을 처리한다
그럼 당연히 “이 linkTo는 어디서 왔지?”라는 궁금증이 생긴다.
바로 여기서 다음 파일로 이어진다.
3. linkTo의 실제 구현체
👉 코드 위치
router-store.tsx — 202번째 줄
https://github.com/expo/router/blob/main/packages/expo-router/src/global-state/router-store.tsx#L202
여기에 linkTo의 실제 구현이 있다.

즉, Link 컴포넌트가 실제 라우팅을 실행할 때 사용하는 로직은 바로 이곳이라는 뜻.
여기서 한 가지 중요한 걸 알 수 있다.
“linkTo는 전역 상태 기반 router-store에서 제공하는 함수다.”
그리고 이 router-store는 아래처럼 전역으로 설정되어 있다.
4. router-store에서 router 객체를 직접 만든다
👉 코드 위치
router-store.tsx — 46번째 줄
https://github.com/expo/router/blob/main/packages/expo-router/src/global-state/router-store.tsx#L46
여기서 router 객체가 만들어지고, push/replace/back/linkTo 같은 메서드들이 함께 묶인다.
이 객체가 바로 앱 전체에서 사용하는 공통 Router 구현체가 된다.
5. 그럼 useRouter()는?
이제 질문은 하나 남는다.
“그럼 useRouter()는 어떤 router를 가져오는 거야?”
답은 이 파일에서 찾을 수 있다.
👉 코드 위치
expo-router/hooks.ts — 36~48번째 줄
https://github.com/expo/router/blob/main/packages/expo-router/src/hooks.ts#L36-L48
여기서 useRouter는 router-store를 그대로 불러와서 동일한 router 객체를 반환한다.
즉, 요약하면:
- Link → useLinkToPathProps → linkTo → router-store
- useRouter → router-store
결론적으로 Link와 useRouter는 같은 router-store를 공유하고 동일한 라우팅 로직을 쓴다.
6. 둘의 차이점은 딱 하나
내부 구현은 같지만, 웹 환경에서의 이벤트 처리가 다르다.
관련 코드:
useLinkToPathProps.tsx – eventShouldPreventDefault
https://github.com/expo/router/blob/main/packages/expo-router/src/link/useLinkToPathProps.tsx
여기서 웹 브라우저 특화 로직이 들어간다.
예를 들면:
- cmd + click → 새 탭 열기
- shift + click → 새 창 열기
- middle click → 새 탭 열기
- 기본 클릭 → router로 이동
즉, Link는 웹 브라우저에서의 링크 클릭 행동을 그대로 지원하기 위해 이벤트를 필터링한다.
반대로 useRouter().push()는 그런 브라우저 편의 기능 없이 바로 push만 실행한다.
정리하면 이렇게 된다:
✔ Link와 useRouter가 사용하는 router는 같다
둘 다 router-store 내부의 동일한 router 객체를 참조하고 navigation 액션도 동일한 함수를 쓴다.
✔ 차이는 “이벤트 처리”에만 있다
Link는 웹 클릭 이벤트(Ctrl/Cmd/Shift/Middle click)를 고려한 브라우저 친화적인 처리를 해준다.
useRouter는 그냥 push/replace/back 같은 라우터 기능만 제공한다.
✔ Link는 UI + event layer
✔ useRouter는 pure routing API layer
이 구조 덕분에 Expo Router는 웹과 앱을 모두 지원하면서도 같은 라우팅 경험을 제공할 수 있다.