React Navigation 의 Linking 이란?

eeennsu·2026년 7월 8일

React Native

목록 보기
65/77

1. URL 스킴이란 무엇인가

URL의 맨 앞 scheme:// 부분이 스킴(scheme) 이다. 어떤 프로토콜/대상으로 이 URL을 처리할지 가리키는 이름표다.

https://example.com/user/42
└─┬─┘
 scheme

example://user/42
└──┬──┘
  scheme

https는 브라우저가 처리하도록 OS에 예약된 표준 스킴이고, example:// 같은 임의의 이름은 커스텀 URL 스킴(custom URL scheme) 이다. 앱이 "이 스킴은 내가 처리하겠다"고 OS에 등록해 두면, 그 스킴으로 시작하는 링크가 눌렸을 때 OS가 해당 앱을 띄워준다. 이게 딥링크의 가장 기본적인 형태다.



2. OS는 스킴을 어떻게 인지하는가 — "설치 시점 등록"

가장 헷갈리기 쉬운 지점이다. 스킴은 앱 코드(JS) 안이 아니라, 앱 번들의 메타데이터에 선언되고, 설치 시점에 OS에 등록된다.

  • iOS는 Info.plist
  • Android는 AndroidManifest.xml

이 파일들은 JS 코드가 아니라 앱 패키지에 포함된 정적 메타데이터다. 앱을 설치하는 순간 OS가 이 파일을 읽어, 시스템 레지스트리에 "example:// 스킴 → 이 앱" 매핑을 저장한다. iOS는 Launch Services, Android는 PackageManager가 이 역할을 한다.

앱 설치
   ↓
OS가 Info.plist / AndroidManifest.xml 을 읽는다
   ↓
시스템 레지스트리에 "example:// → 이 앱" 등록
   ↓
(앱을 한 번도 실행하지 않아도 OS는 이미 매핑을 안다)
   ↓
나중에 example://user/42 링크가 눌리면 → OS가 앱을 깨운다

즉 OS는 앱에 "들어가서" 아는 게 아니다. 설치 때 미리 등록받아 두기 때문에, 앱이 실행 중이 아니어도, 한 번도 켜진 적 없어도 그 스킴이 어느 앱 것인지 이미 안다.


두 곳의 스킴 선언을 혼동하지 말 것

딥링크를 다루다 보면 스킴을 두 군데에 적게 되는데, 역할이 완전히 다르다.

적는 곳누구를 위한 것인가언제 쓰이나
Info.plist / AndroidManifest.xmlOS용설치 시점, OS가 읽어 등록
React Navigation linking.prefixes앱 내부(JS)용OS가 URL을 앱에 넘긴 다음, 화면 매칭

이 둘은 별개 장소에 적히고, 같은 문자열이어야 링크가 끝까지 흐른다. 하나라도 어긋나면 링크가 앱에 도달하지 않거나, 도달해도 매칭되지 않는다.



3. 커스텀 스킴의 근본 문제 — 소유권 보장이 없다

example:// 같은 커스텀 스킴은 전역 소유권이 전혀 보장되지 않는다. 누구나 같은 스킴을 등록할 수 있다. 두 앱이 같은 스킴을 등록하면 이렇게 된다.

  • iOS : 어느 앱이 열릴지 정의되지 않는다(undefined behavior). Apple이 보장하지 않는다. 과거 악성 앱이 인기 앱의 스킴을 가로채는 하이재킹 사례가 이 때문에 가능했다.
  • Android : 같은 스킴/intent-filter를 가진 앱이 여러 개면 OS가 앱 선택 다이얼로그("어떤 앱으로 열까요?")를 띄운다. 사용자가 잘못 선택하면 엉뚱한 앱으로 간다.

완화 관례로 역도메인 표기(com.mycompany.myapp://)를 써서 충돌 확률을 낮추긴 한다. 하지만 이건 어디까지나 충돌을 줄이는 관례일 뿐, OS가 강제하는 소유권 보장이 아니다. 마음먹은 다른 앱이 같은 문자열을 등록하는 걸 막을 방법은 없다.

또 다른 단점도 있다. 커스텀 스킴 링크는 앱이 설치돼 있지 않으면 아무 동작도 하지 않거나 에러가 된다(브라우저가 example://를 어떻게 처리할지 모른다). 웹 폴백이 없다.



4. 근본 해결책 — 유니버설 링크 / 앱 링크

커스텀 스킴의 소유권 문제를 푸는 방식이 유니버설 링크(Universal Links, iOS)앱 링크(App Links, Android) 다. 커스텀 스킴 대신 https://example.com 같은 실제 도메인을 쓴다.

핵심은 도메인 소유권을 증명해야 한다는 것이다.

  • iOS : 서버에 apple-app-site-association(AASA) 파일을 올린다.
  • Android : 서버에 assetlinks.json 파일을 올린다.

OS는 앱 설치 시(또는 검증 시점에) 그 도메인에 실제로 접속해 이 파일을 읽고, "이 앱이 이 도메인의 주인이 맞나"를 검증한다. 도메인은 전역적으로 유일하게 소유되므로, 다른 앱이 내 도메인 링크를 가로챌 수 없다. 충돌 자체가 성립하지 않는다.

추가 이점:

  • 앱이 설치돼 있지 않으면 그냥 웹페이지가 열린다(같은 https:// URL이니까). 자연스러운 폴백이다.
  • 사용자에게 보이는 URL이 정상적인 웹 주소라 신뢰도가 높다.

대신 비용이 있다. 도메인과 서버가 있어야 하고, AASA / assetlinks.json을 정확한 경로와 형식으로 호스팅해야 하며, 설정이 틀리면 검증이 조용히 실패해 디버깅이 까다롭다.



5. 그래서 example:// 같은 단순 스킴, 실무에서 쓰나?

공개 딥링크(사용자에게 공유되는 링크)로는 거의 쓰지 않는다. 충돌·하이재킹·웹 폴백 부재 때문에, 프로덕션의 공유 링크는 유니버설 링크 / 앱 링크로 가는 게 표준이다. 단순한 example://는 더더욱 안 쓰고, 쓰더라도 최소한 역도메인 형태로 바꾼다.

다만 커스텀 스킴이 여전히 유효하게 쓰이는 자리가 있다.

  • OAuth / 소셜 로그인 콜백: com.myapp://oauth/callback 처럼 인증 후 앱으로 돌아오는 리다이렉트 대상으로 흔하다. 외부에 공유되는 링크가 아니라 충돌 위험이 낮다.
  • 개발 / 내부 테스트: 서버·도메인 검증 없이 바로 동작하므로, 매칭 로직을 빠르게 확인할 때 편하다.
  • 앱 간 연동의 약속된 채널: 특정 앱끼리 정해 둔 진입점.
  • 유니버설 링크의 보조: https:// 링크와 함께 등록해 두는 백업 경로.

요약하면, 단순 example://공유 링크의 주력으로 쓰는 건 비권장이고, 콜백·개발·보조 용도로는 지금도 정상적으로 쓰인다.



6. 대안 비교

방식형태소유권/충돌미설치 시설정 난이도주 용도
단순 커스텀 스킴example://보장 없음, 충돌 가능동작 안 함매우 쉬움개발/테스트
역도메인 커스텀 스킴com.myapp://충돌 확률만 낮춤동작 안 함쉬움OAuth 콜백, 보조
유니버설 링크 (iOS)https://example.com도메인 검증으로 보장웹으로 폴백어려움공개 공유 링크
앱 링크 (Android)https://example.com도메인 검증으로 보장웹으로 폴백어려움공개 공유 링크


7. 무엇이 제일 좋은가 — 권장

공개 딥링크의 정답은 유니버설 링크(iOS) + 앱 링크(Android)다. 도메인 소유권으로 충돌과 하이재킹을 원천 차단하고, 미설치 시 웹 폴백까지 되기 때문이다. 사용자에게 공유되는 모든 링크는 이걸 기본으로 둔다.

그리고 커스텀 스킴(역도메인 형태)을 보조로 함께 등록하는 게 실무의 일반적인 조합이다. OAuth 콜백, 내부 진입점, 개발 편의 같은 곳에서 여전히 유용하기 때문이다.

React Navigation 입장에서는 둘을 구분하지 않는다. prefixes에 그냥 둘 다 적어두면, 어느 쪽으로 들어온 URL이든 동일한 화면 매칭 규칙을 탄다.

const linking = {
  prefixes: [
    'https://example.com', // 유니버설 링크 / 앱 링크 (주력, 공유용)
    'com.mycompany.myapp://', // 커스텀 스킴 (보조, 콜백·개발용)
  ],
  enabled: 'auto',
};

정리: example:// 같은 단순 스킴은 학습·개발에는 편하지만 공개 링크의 주력으로는 부적합하다. 프로덕션 공유 링크는 유니버설/앱 링크를 주력으로, 역도메인 커스텀 스킴을 보조로 두는 조합이 가장 안전하다.



8. 한 가지 주의 — prefixes와 네이티브 등록은 항상 짝

어떤 방식을 고르든 원칙은 같다. linking.prefixes에 적은 값과 네이티브에 등록한 스킴/도메인이 정확히 일치해야 링크가 끝까지 흐른다.

  • 커스텀 스킴: Info.plist(URL Types) / AndroidManifest.xml(intent-filter)에 등록한 스킴 = prefixes의 스킴
  • 유니버설/앱 링크: Associated Domains + AASA(iOS) / autoVerify intent-filter + assetlinks.json(Android)에 설정한 도메인 = prefixes의 도메인

OS 등록(설치 시점)과 앱 내부 매칭(prefixes)은 별개 단계이고, 둘 다 갖춰져야 동작한다는 점을 기억하면 된다.

profile
이력서 https://resume.eunsu.pro

0개의 댓글