딥링크가 중요한 이유
마케팅 메일에서 "상품 보러가기" 링크를 눌렀더니 모바일 웹이 열리고, 다시 "앱에서 열기" 버튼을 눌러야 앱으로 넘어가는 경험을 해보셨을 겁니다. 이 한 번의 우회가 전환율을 30% 가까이 떨어뜨린다는 게 Branch.io의 2023년 리포트 결론입니다. 사용자는 의지가 식고, 광고비는 낭비됩니다.
올바르게 구현된 딥링크는 사용자가 클릭한 그 순간 앱이 즉시 정확한 화면으로 열립니다. 앱이 없으면 자연스럽게 웹으로 폴백하고, 앱을 새로 설치한 첫 실행 때도 원래 가려던 화면으로 직행시킬 수 있습니다(이걸 deferred deep link라고 부릅니다).
세 가지 방식 — 무엇을 골라야 하나
| 방식 | 형식 | 장점 | 한계 |
|---|---|---|---|
| URL Scheme | myapp://product/123 | 구현 쉬움 | 앱 미설치 시 에러, 충돌 가능 |
| Universal Links (iOS) | https://example.com/p/123 | HTTPS, 자연 폴백 | AASA 파일 + Apple 검증 필요 |
| App Links (Android) | https://example.com/p/123 | 동일 원리 | assetlinks.json + autoVerify |
2026년 운영 기준에서는 Universal Links + App Links 조합이 표준입니다. URL Scheme은 인앱 링크용 보조 수단으로만 남기는 게 좋습니다.
iOS Universal Links 설정
도메인의 https://example.com/.well-known/apple-app-site-association 경로에 다음 JSON을 두면 됩니다(MIME은 application/json, 리다이렉트 금지).
{
"applinks": {
"details": [{
"appIDs": ["TEAMID.com.example.app"],
"components": [
{ "/": "/products/*", "comment": "상품 페이지" },
{ "/": "/u/*", "comment": "유저 프로필" },
{ "/": "/legal/*", "exclude": true }
]
}]
}
}
Xcode 프로젝트에서는 Signing & Capabilities → Associated Domains에 applinks:example.com을 추가합니다. iOS는 앱 설치 시 이 파일을 한 번 가져와 검증하고 캐싱합니다. 검증 실패가 가장 흔한 디버깅 항목이니 swcutil dl이나 시뮬레이터의 xcrun simctl openurl 명령으로 확인합니다.
Android App Links 설정
/.well-known/assetlinks.json을 같은 형식으로 둡니다.
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["AB:CD:..."]
}
}]
AndroidManifest.xml의 인텐트 필터에 android:autoVerify="true"를 붙이면 OS가 설치 시 이 파일을 자동 검증합니다.
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="example.com" android:pathPrefix="/products"/>
</intent-filter>
React Native·Flutter에서의 라우팅
OS가 앱을 띄우고 나면 라우터가 URL을 받아 화면을 띄워야 합니다. React Navigation v6에는 linking 옵션이 있습니다.
const linking = {
prefixes: ["https://example.com", "myapp://"],
config: {
screens: {
Product: "products/:id",
Profile: "u/:username",
},
},
};
<NavigationContainer linking={linking}>...
Flutter는 go_router의 redirect 콜백 또는 uni_links 패키지로 동일한 결과를 냅니다.
Deferred Deep Link — 설치 후 원래 자리로
가장 까다로운 시나리오: 사용자가 링크를 클릭했는데 앱이 없어서 스토어로 갔고, 설치 후 처음 실행했을 때 원래 가려던 화면으로 데려가는 것.
순수 OS API만으로는 이 정보가 끊깁니다. 그래서 Branch·AppsFlyer·Firebase Dynamic Links 같은 SDK를 쓰거나, 직접 만든다면 다음 흐름을 씁니다.
- 링크에
?fp=...같은 핑거프린트(IP+UA+해상도)를 추가해 백엔드 임시 저장 - 앱이 처음 실행되면 같은 핑거프린트로 백엔드 조회
- 매칭되면 원래 URL로 라우팅
핑거프린트는 정확도가 70~90%이므로, 정밀해야 하면 클립보드 토큰이나 설치 후 첫 실행 시 사용자에게 "이전 화면으로 이동" 버튼을 보여주는 보조 UX를 곁들입니다.
다음 단계
딥링크는 한 번 잘 깔아두면 앱마케팅·이메일·SMS·QR코드·푸시 모두 같은 URL로 통합됩니다. 다음으로는 App Clips(iOS)·Instant Apps(Android) 로 설치 없이 앱 일부만 실행시키는 흐름, 그리고 푸시 알림 클릭 시 정확한 화면 라우팅까지 묶어두면 모바일 사용자 여정 전반이 하나의 URL 모델로 정리됩니다.