오늘도 버터
딥링크 정의와 종류: URI 스키마·Universal Links·Deferred Deep Link 본문
안녕하세요, 개발자 버터입니다. 🧈
광고 배너를 눌렀는데 앱스토어가 뜨고, 설치 후 다시 광고를 찾아서 원하던 상품 페이지를 열어야 했던 경험이 있으실 겁니다.
혹은 반대로, 링크를 눌렀더니 앱이 바로 열리면서 원하는 화면으로 딱 이동했던 경험도요.
이 두 경험의 차이를 만드는 게 딥링크입니다.
저희 팀도 처음엔 이랬습니다. 직접 파고들수록 생각보다 복잡한 문제라는 걸 알게 됐고요.
이 글에서는 딥링크가 뭔지, 구현 방식이 어떻게 발전해왔는지, 그리고 결국 저희가 직접 구현 대신 서드파티 솔루션을 선택하게 된 맥락을 정리해봤습니다.
• • •
딥링크가 필요한 이유
딥링크(Deep Link)는 단순히 앱을 여는 것을 넘어, 앱 내 특정 화면으로 사용자를 직접 데려다주는 링크입니다.
마케팅 관점에서 보면 이건 꽤 중요합니다. 사용자가 광고에서 본 상품에 관심을 가졌는데, 앱을 열었더니 홈 화면이 나온다면? 그 사용자의 상당수는 그냥 이탈합니다.
딥링크가 없으면 광고 클릭에서 실제 전환까지의 마찰이 커집니다.
URL 파라미터로 데이터도 전달할 수 있어서, 초대 코드나 프로모션 코드를 링크에 담아 앱 실행 시점에 자동 적용하는 것도 딥링크로 구현합니다.
Slack이 링크로 열릴 때 해당 채널로 바로 이동하거나, 게임 앱에서 친구 초대 링크를 누르면 초대한 사람 정보가 앱에 전달되는 것도 같은 원리입니다.
딥링크 구현 방식의 역사
딥링크 구현 방식은 하나가 아닙니다. 문제를 해결하려다 새로운 문제가 생기고, 그걸 해결하려다 또 다른 방식이 나왔습니다.
순서대로 이해하면 왜 지금의 방식이 표준이 됐는지 자연스럽게 납득이 됩니다.
1단계: URI 스키마 — 가장 단순한 방식
처음 나온 방식은 커스텀 URL 스킴입니다. myapp://product/456 처럼 앱만의 고유한 스킴을 정의하고, OS가 해당 스킴을 가진 앱을 찾아 실행하는 방식입니다.
설정도 간단합니다.
- iOS:
AppDelegate에 메서드 하나 추가 - Android:
AndroidManifest.xml에intent-filter몇 줄 추가
그런데 이 방식에는 구조적인 문제가 있습니다.
URI 스키마의 한계
스킴 중복 문제
myapp:// 스킴을 누가 먼저 등록했는지 OS는 알지 못합니다. 다른 앱이 동일한 스킴을 등록하면, 사용자가 링크를 탭했을 때 의도치 않은 앱이 열릴 수 있습니다. 악의적으로 활용하면 피싱도 가능합니다.
앱 미설치 시 오류
앱이 없으면 브라우저에 에러 페이지만 뜹니다. 앱스토어로 안내하거나 웹페이지로 연결하는 기능이 없습니다.
Apple은 iOS 9에서 Universal Links를 도입하며 HTTPS 기반 딥링크 방식을 공식 표준으로 밀기 시작했습니다.
iOS 9.2부터는 Safari에서 커스텀 URL Scheme 처리 방식이 바뀌었는데, 이게 꽤 치명적이었습니다.
기존에는 "앱이 없으면 앱스토어로 보내는" 우회 패턴이 있었습니다. 링크를 탭하면 먼저 커스텀 스킴으로 앱 열기를 시도하고, 일정 시간 안에 반응이 없으면 JS 타이머로 앱스토어 URL로 리다이렉트하는 방식이었습니다.
iOS 9.2부터는 Safari가 커스텀 스킴 탭 시 바로 모달을 띄워 JS 타이머 자체를 블로킹해버리면서, 이 패턴이 완전히 깨졌습니다. 앱이 없는 사용자는 그냥 "이 페이지를 열 수 없습니다" 오류 화면을 보게 됩니다.
커스텀 스킴이 작동을 멈춘 건 아니지만, 신규 앱에서는 그냥 안 쓰는 걸 추천합니다.
2단계: Universal Links / App Links — 도메인 기반 딥링크
URI 스키마의 보안 문제를 해결하기 위해 Apple은 iOS 9에서 Universal Links를, Google은 Android 6.0에서 App Links를 도입했습니다. 이름은 다르지만 개념은 같습니다.
핵심 아이디어는 간단합니다. 커스텀 스킴 대신 HTTPS 도메인을 딥링크 URL로 사용한다.
myapp://product/456 대신 https://example.com/product/456 을 그대로 딥링크로 씁니다.
도메인은 소유자만 설정을 변경할 수 있으니, 다른 앱이 같은 링크를 가로채는 건 애초에 불가능합니다.
동작 원리는 이렇습니다. 앱을 설치할 때 iOS/Android OS가 서버에 미리 올려둔 검증 파일을 다운로드해서 "이 도메인의 링크는 이 앱과 연결된다"는 것을 등록해둡니다.
- iOS:
https://example.com/.well-known/apple-app-site-association - Android:
https://example.com/.well-known/assetlinks.json
이후 해당 도메인 링크를 탭하면 브라우저 대신 앱이 바로 열립니다. 앱이 없으면 원래 URL이 가리키는 웹페이지로 자연스럽게 연결됩니다. URI 스키마의 오류 문제가 해결되는 거죠.
React Native에서는 Linking API로 JS 레이어에서 URL을 수신합니다. 앱 상태에 따라 두 가지 방식으로 처리합니다.
- 앱 실행 중:
addEventListener - 앱 종료 상태에서 딥링크로 실행된 경우:
getInitialURL
두 케이스를 모두 구현하지 않으면 특정 상황에서 딥링크가 무시되니 주의해야 합니다.
React Native Linking API — 실행 중 / 종료 상태 딥링크 모두 처리
import { useEffect } from 'react';
import { Linking } from 'react-native';
useEffect(() => {
// 앱 실행 중 딥링크 수신
const subscription = Linking.addEventListener('url', ({ url }) => {
handleDeepLink(url);
});
// 앱 종료 상태에서 딥링크로 실행된 경우
Linking.getInitialURL().then(url => {
if (url) handleDeepLink(url);
});
return () => subscription.remove();
}, []);
3단계: Deferred Deep Link — 설치 전후를 연결하다
여기까지 구현하면 "앱이 설치된 사용자"에 대한 딥링크는 해결됩니다. 그런데 마케팅에서 가장 중요한 케이스가 빠져 있습니다. 앱이 없는 신규 사용자입니다.
상황을 그려보면 이렇습니다.
- 사용자가 광고 링크를 탭함
- 앱이 없으니 앱스토어로 이동
- 앱을 설치하고 실행
- 홈 화면이 열림 ← 딥링크 정보가 사라짐
앱스토어로 이동하는 과정에서 딥링크 정보가 사라집니다. 어떤 광고로 유입됐는지, 어떤 상품을 보고 있었는지, 어떤 초대 코드를 가지고 왔는지 — 전부 알 수 없게 됩니다.
이걸 해결하는 게 Deferred Deep Link(지연된 딥링크)입니다. 링크를 탭하는 시점에 딥링크 정보를 서버에 저장하고, 앱 설치 후 첫 실행 시 그 정보를 불러와 원래 목적지로 라우팅합니다.
"deferred(지연된)"라는 이름이 이 동작을 잘 설명합니다.
직접 구현하면 어떤 문제가 생기나
Universal Links, App Links, Deferred Deep Link까지 직접 구현하면 이론적으로는 가능합니다. 하지만 현실적으로 몇 가지 벽에 부딪힙니다.
직접 구현의 문제점
파편화된 관리
iOS 검증 파일, Android 검증 파일, URI 스킴 설정, JS 레이어 처리 — 각각 따로 관리해야 합니다. 어느 하나 틀리면 특정 플랫폼/버전에서만 딥링크가 안 열리는데, 원인 파악이 쉽지 않습니다.
iOS Deferred Deep Link의 정확도 한계
Android는 Google Play의 Install Referrer API로 공식 지원하지만, iOS는 공식 메커니즘이 없어 IP + 기기 정보 조합의 핑거프린팅 방식을 씁니다. 직접 구현하면 플랫폼 정책 변화에 바로 영향을 받고, 정확도도 장담하기 어렵습니다.
그래서 저희도 이 복잡성을 추상화해주는 서드파티 솔루션인 Firebase Dynamic Links를 택했습니다. 플랫폼별 검증 파일 자동 생성, Deferred Deep Link 처리, 유입 어트리뷰션까지 한 번에 해결해주는 서비스였습니다.
그런데 결정적인 일이 있었습니다. 저희가 쓰던 Firebase Dynamic Links가 2025년 8월 25일 서비스를 종료했습니다.
Deferred Deep Link까지 커버해주던 솔루션이 사라지니, 그냥 대안을 찾는 게 아니라 구조 전체를 다시 잡아야 했습니다. 다음 글에서 그 과정을 다룹니다.
• • •
정리
딥링크 구현 방식의 흐름을 한 줄씩 정리하면 이렇습니다.
- URI 스키마: 간단하지만 보안 취약, 앱 미설치 시 오류. 프로토타이핑에는 괜찮지만 프로덕션 단독 사용은 부적합
- Universal Links / App Links: 도메인 기반으로 보안과 fallback 해결. iOS/Android 각각 서버 파일 설정 필요
- Deferred Deep Link: 신규 사용자 전환에 필수. 직접 구현은 복잡하고 서드파티 솔루션이 현실적
- Firebase Dynamic Links: 2025년 8월 25일 종료. 현재 사용 중이라면 마이그레이션 필수
결국 딥링크는 링크 하나의 문제가 아니라, 사용자가 어디서 왔고 어디로 가야 하는지를 끊김 없이 연결하는 인프라에 가깝습니다.
다음 글에서는 AppsFlyer OneLink로 이 인프라를 어떻게 구성했는지, 실제 도입 과정을 구체적으로 다룹니다.
참고 자료
'React-Native' 카테고리의 다른 글
| [앱 개발 준비물] 애플 개발자 계정, 안드로이드 개발자 계정: 회사 계정으로 가입하기 (0) | 2025.12.30 |
|---|---|
| Android 앱 시작 방식: 콜드 스타트, 웜 스타트, 핫 스타트 (2) | 2025.12.27 |