딥링크가 안 되는 흔한 이유
딥링크 설정은 한 곳이라도 빠지면 전체가 안 되죠. iOS와 Android 각각 설정 파일, 서버 파일, 앱 코드 세 곳을 모두 맞춰야 합니다.
가장 흔한 실수, AASA(Apple App Site Association) 파일의 JSON 오류이죠.
가장 흔한 실수, AASA(Apple App Site Association) 파일의 JSON 오류이죠.
iOS Universal Links 체크리스트
Associated Domains 설정, AASA 파일 호스팅, 앱 entitlements 파일. 이 세 가지가 모두 일치해야 합니다.
json
// 1. Runner.entitlements에 도메인 추가
// Xcode > Signing & Capabilities > Associated Domains
// applinks:example.com
// 2. AASA 파일 (서버: https://example.com/.well-known/apple-app-site-association)
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["TEAM_ID.com.example.myapp"],
"components": [
{
"/": "/games/*",
"comment": "게임 상세 딥링크"
}
]
}
]
}
}
// 흔한 실수:
// - appIDs에 TeamID 빠뜨림
// - AASA 파일 Content-Type이 application/json이 아님
// - https가 아닌 http로 호스팅Android App Links 검증
assetlinks.json 파일과 AndroidManifest.xml의 intent-filter가 일치해야 합니다. SHA256 인증서 핑거프린트 불일치가 가장 흔한 원인입니다.
yaml
<!-- AndroidManifest.xml -->
<activity android:name=".MainActivity">
<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="/games" />
</intent-filter>
</activity>json
// .well-known/assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": [
"AA:BB:CC:..." // debug와 release 키가 다름!
]
}
}]
// SHA256 핑거프린트 확인
// keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkeyadb로 딥링크 테스트
실제 브라우저에서 링크를 탭하는 것과 adb로 인텐트를 보내는 것은 동작이 다릅니다. 둘 다 테스트해야 돼요.
bash
# Android 딥링크 테스트
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://example.com/games/detail?gameId=abc123"
# App Links 검증 상태 확인
adb shell pm get-app-links com.example.myapp
# iOS 딥링크 테스트 (시뮬레이터)
xcrun simctl openurl booted "https://example.com/games/detail?gameId=abc123"GoRouter redirect와 딥링크 충돌
인증 체크를 위한 redirect 로직이 딥링크를 가로채는 경우가 있습니다. 앱이 아직 초기화 중일 때 딥링크가 들어오면 redirect가 먼저 실행되죠.
해결법은 딥링크 경로를 저장해두고, 인증 완료 후 이동하는 겁니다.
해결법은 딥링크 경로를 저장해두고, 인증 완료 후 이동하는 겁니다.
Dart
// redirect에서 딥링크 보존
String? _pendingDeepLink;
redirect: (context, state) {
final isLoggedIn = ref.read(authProvider).isLoggedIn;
final location = state.matchedLocation;
final isAuthRoute = location == '/login' || location == '/signup';
if (!isLoggedIn && !isAuthRoute) {
// 딥링크 경로 저장
_pendingDeepLink = state.uri.toString();
return '/login';
}
// 로그인 완료 후 저장된 딥링크로 이동
if (isLoggedIn && isAuthRoute && _pendingDeepLink != null) {
final link = _pendingDeepLink;
_pendingDeepLink = null;
return link;
}
return null;
}Firebase Dynamic Links 대안
Firebase Dynamic Links는 2025년 8월에 종료되었어요. 대안으로 Branch.io나 직접 구현을 고려해야 돼요.
단순한 딥링크라면 직접 AASA/assetlinks.json을 호스팅하는 게 가장 깔끔해요. 어트리뷰션 추적까지 필요하면 Branch.io나 Adjust를 쓰세요.
단순한 딥링크라면 직접 AASA/assetlinks.json을 호스팅하는 게 가장 깔끔해요. 어트리뷰션 추적까지 필요하면 Branch.io나 Adjust를 쓰세요.