개발

[rn-expo] Link와 useRouter는 결국 같은 구현체였다

woojin06 2025. 12. 9. 17:30

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는 웹과 앱을 모두 지원하면서도 같은 라우팅 경험을 제공할 수 있다.