개요
모바일 앱 개발이나 마케팅에서 자주 등장하는 용어들입니다. 비슷해 보이지만 각각 다른 개념이고, 동작 방식도 다릅니다.
딥링크 (Deep Link)
앱 내 특정 화면으로 바로 이동하는 링크입니다.
URI Scheme 방식
가장 기본적인 딥링크 방식입니다.
myapp://product/123
myapp://user/profile
twitter://user?screen_name=elonmusk장점
- 구현이 간단함
- 앱에서 직접 스킴 등록만 하면 됨
단점
- 앱이 설치되어 있지 않으면 동작하지 않음 (에러 발생)
- 스킴 충돌 가능 (다른 앱과 같은 스킴 사용 시)
- 웹에서 검증 불가
동작 흐름
- 사용자가
myapp://product/123클릭 - OS가
myapp스킴을 처리할 앱 검색 - 앱이 있으면 → 앱 실행 + 해당 화면으로 이동
- 앱이 없으면 → 에러 또는 아무 반응 없음
유니버셜 링크 (Universal Links) - iOS
**iOS 9+**에서 도입된 Apple의 딥링크 솔루션입니다. 일반 HTTPS URL을 사용합니다.
https://example.com/product/123동작 원리
- 앱이 설치되어 있으면 → 앱에서 열림
- 앱이 없으면 → 웹 브라우저에서 해당 URL 열림 (Fallback)
설정 방법
1. 서버에 apple-app-site-association 파일 배포
// https://example.com/.well-known/apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.example.app",
"paths": ["/product/*", "/user/*"]
}
]
}
}2. 앱에서 Associated Domains 설정
applinks:example.com3. iOS가 앱 설치 시 해당 도메인에서 파일 다운로드 & 캐싱
특징
- HTTPS만 지원
- Apple CDN이 파일을 캐싱 (앱 설치/업데이트 시 갱신)
- 같은 도메인의 웹페이지에서 링크 클릭 시 동작 안 함 (Safari 정책)
앱 링크 (App Links) - Android
**Android 6.0+**에서 도입된 Google의 딥링크 솔루션입니다. 유니버셜 링크와 유사한 개념입니다.
https://example.com/product/123설정 방법
1. 서버에 assetlinks.json 파일 배포
// https://example.com/.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:EF:..."]
}
}]2. AndroidManifest.xml에 intent-filter 추가
<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" />
</intent-filter>특징
android:autoVerify="true"설정 시 자동 검증- 검증 실패 시 일반 딥링크처럼 앱 선택 다이얼로그 표시
디퍼드 딥링크 (Deferred Deep Link)
앱이 설치되어 있지 않아도 동작하는 딥링크입니다.
동작 흐름
- 사용자가 링크 클릭
- 앱이 없으면 → 앱스토어로 이동
- 앱 설치 후 첫 실행 시 → 원래 의도했던 화면으로 이동
구현 방식
- 클립보드: 링크 정보를 클립보드에 저장 → 앱 실행 시 읽기
- 핑거프린팅: 디바이스 정보 기반 매칭 (IP, User-Agent 등)
- 서드파티 서비스: Branch, AppsFlyer, Firebase Dynamic Links 등
비교 표
| 구분 | URI Scheme | Universal Links (iOS) | App Links (Android) |
|---|---|---|---|
| 형식 | myapp://path |
https://domain/path |
https://domain/path |
| 앱 미설치 시 | 에러/무반응 | 웹으로 Fallback | 웹으로 Fallback |
| 검증 방식 | 없음 | AASA 파일 | assetlinks.json |
| 보안 | 낮음 (스킴 탈취 가능) | 높음 (도메인 소유권 검증) | 높음 (도메인 소유권 검증) |
| 최소 버전 | - | iOS 9+ | Android 6.0+ |
실무 팁
1. 둘 다 설정하라
유니버셜 링크/앱 링크가 안 되는 상황이 있습니다:
- 앱 내 웹뷰에서 클릭
- 일부 SNS 앱의 인앱 브라우저
- 사용자가 “브라우저에서 열기” 선택
→ URI Scheme을 백업으로 함께 구현
2. 테스트 도구
# iOS - AASA 파일 검증
curl -I https://example.com/.well-known/apple-app-site-association
# Android - Digital Asset Links 검증
https://developers.google.com/digital-asset-links/tools/generator3. 디버깅
- iOS:
Settings > Developer > Universal Links에서 진단 - Android:
adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/path"
Flutter에서 딥링크 구현
Flutter에서는 여러 방식으로 딥링크를 처리할 수 있습니다.
1. go_router 사용 (권장)
// pubspec.yaml
dependencies:
go_router: ^12.0.0final router = GoRouter(
routes: [
GoRoute(
path: '/product/:id',
builder: (context, state) {
final productId = state.pathParameters['id'];
return ProductScreen(id: productId!);
},
),
],
);2. 네이티브 설정
iOS - Info.plist
<!-- URI Scheme -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>iOS - Associated Domains (Runner.entitlements)
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>Android - AndroidManifest.xml
<activity android:name=".MainActivity">
<!-- URI Scheme -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
<!-- App Links -->
<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" />
</intent-filter>
</activity>3. 앱 링크 수신 처리
import 'package:app_links/app_links.dart';
class DeepLinkHandler {
final _appLinks = AppLinks();
void init() {
// 앱이 이미 실행 중일 때 딥링크 수신
_appLinks.uriLinkStream.listen((Uri uri) {
_handleDeepLink(uri);
});
// 앱이 딥링크로 시작된 경우
_appLinks.getInitialAppLink().then((uri) {
if (uri != null) {
_handleDeepLink(uri);
}
});
}
void _handleDeepLink(Uri uri) {
// /product/123 -> ProductScreen으로 이동
if (uri.pathSegments.isNotEmpty && uri.pathSegments[0] == 'product') {
final productId = uri.pathSegments[1];
// Navigator 또는 GoRouter로 이동
}
}
}4. 유용한 패키지
| 패키지 | 설명 |
|---|---|
go_router |
선언적 라우팅 + 딥링크 자동 처리 |
app_links |
딥링크 수신 처리 (uni_links 대체) |
firebase_dynamic_links |
Firebase Dynamic Links 연동 |
5. 테스트 방법
# iOS 시뮬레이터
xcrun simctl openurl booted "myapp://product/123"
xcrun simctl openurl booted "https://example.com/product/123"
# Android 에뮬레이터
adb shell am start -W -a android.intent.action.VIEW -d "myapp://product/123"
adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/product/123"관련 용어
- Deferred Deep Link: 앱 미설치 상태에서도 설치 후 특정 화면으로 이동
- Dynamic Links (Firebase): Google의 디퍼드 딥링크 솔루션 (2025년 지원 종료 예정)
- Branch.io: 대표적인 딥링크 서드파티 서비스