딥링킹이란 무엇인가
딥링킹(Deep Linking)은 사용자를 앱의 특정 화면이나 콘텐츠로 직접 이동시키는 기술입니다. 웹의 URL과 유사한 개념으로, 모바일 앱에서도 특정 리소스에 직접 접근할 수 있게 해줍니다.
딥링킹이 필요한 실제 상황:
- 푸시 알림: "새 메시지가 도착했습니다" 알림을 탭하면 해당 채팅방으로 직접 이동
- 이메일 마케팅: 프로모션 이메일의 링크를 통해 앱 내 특정 상품 페이지로 이동
- 소셜 미디어 공유: Instagram에 공유된 링크로 앱의 특정 게시물 열기
- QR 코드: 오프라인 매장의 QR 코드로 앱 내 쿠폰 페이지 접근
- 앱 간 연동: 카카오톡에서 배달 앱의 특정 메뉴로 바로 이동
딥링킹이 필요한 실제 상황:
- 푸시 알림: "새 메시지가 도착했습니다" 알림을 탭하면 해당 채팅방으로 직접 이동
- 이메일 마케팅: 프로모션 이메일의 링크를 통해 앱 내 특정 상품 페이지로 이동
- 소셜 미디어 공유: Instagram에 공유된 링크로 앱의 특정 게시물 열기
- QR 코드: 오프라인 매장의 QR 코드로 앱 내 쿠폰 페이지 접근
- 앱 간 연동: 카카오톡에서 배달 앱의 특정 메뉴로 바로 이동
딥링킹의 세 가지 유형
딥링킹은 구현 방식과 동작 원리에 따라 세 가지 주요 유형으로 분류됩니다.
1. 전통적 딥링크 (URI Scheme)
URI 스킴은 가장 오래되고 간단한 딥링킹 방식입니다. 커스텀 URL 스킴을 사용하여 앱을 식별하고 실행합니다.
- 형식:
- 장점: 구현이 매우 간단하고 모든 OS 버전에서 지원
- 단점: 앱이 설치되어 있지 않으면 오류 발생, 다른 앱이 동일한 스킴을 사용할 수 있어 보안 취약
2. 유니버설 링크 (iOS) / 앱 링크 (Android)
일반 웹 URL을 사용하되, 앱이 설치되어 있으면 앱으로, 없으면 웹사이트로 연결되는 스마트한 방식입니다.
- 형식:
- 장점: 웹 폴백 지원, SEO 친화적, 보안성 높음, 앱 설치 유무와 관계없이 작동
- 단점: 도메인 소유권 인증 필요, 설정이 복잡, 서버 구성 필요
3. 디퍼드 딥링크 (Deferred Deep Link)
앱이 설치되어 있지 않은 경우, 사용자가 앱을 설치한 후에도 원래 의도했던 콘텐츠로 이동시키는 고급 기술입니다.
- 동작 과정: 링크 클릭 → 앱스토어로 이동 → 앱 설치 → 첫 실행 시 원래 목적지로 자동 이동
- 장점: 완벽한 사용자 경험 제공, 마케팅 캠페인에 매우 효과적
- 단점: 구현이 복잡, 보통 Firebase Dynamic Links 같은 서드파티 서비스 필요
1. 전통적 딥링크 (URI Scheme)
URI 스킴은 가장 오래되고 간단한 딥링킹 방식입니다. 커스텀 URL 스킴을 사용하여 앱을 식별하고 실행합니다.
- 형식:
myapp://product/123
또는 myapp://user/profile
- 장점: 구현이 매우 간단하고 모든 OS 버전에서 지원
- 단점: 앱이 설치되어 있지 않으면 오류 발생, 다른 앱이 동일한 스킴을 사용할 수 있어 보안 취약
2. 유니버설 링크 (iOS) / 앱 링크 (Android)
일반 웹 URL을 사용하되, 앱이 설치되어 있으면 앱으로, 없으면 웹사이트로 연결되는 스마트한 방식입니다.
- 형식:
https://myapp.com/product/123
- 장점: 웹 폴백 지원, SEO 친화적, 보안성 높음, 앱 설치 유무와 관계없이 작동
- 단점: 도메인 소유권 인증 필요, 설정이 복잡, 서버 구성 필요
3. 디퍼드 딥링크 (Deferred Deep Link)
앱이 설치되어 있지 않은 경우, 사용자가 앱을 설치한 후에도 원래 의도했던 콘텐츠로 이동시키는 고급 기술입니다.
- 동작 과정: 링크 클릭 → 앱스토어로 이동 → 앱 설치 → 첫 실행 시 원래 목적지로 자동 이동
- 장점: 완벽한 사용자 경험 제공, 마케팅 캠페인에 매우 효과적
- 단점: 구현이 복잡, 보통 Firebase Dynamic Links 같은 서드파티 서비스 필요
Android 플랫폼 설정
Android에서 딥링킹을 구현하려면 AndroidManifest.xml 파일을 수정해야 합니다. 각 설정의 의미와 목적을 상세히 알아보겠습니다.
xml
<!-- android/app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- 기본 런처 인텐트 필터 -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- 커스텀 URL 스킴 설정 -->
<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="myflutterapp" />
<data android:scheme="myapp" />
</intent-filter>
<!-- App Links 설정 (Android 6.0+) -->
<!-- autoVerify="true"는 자동으로 도메인 소유권을 확인 -->
<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" />
<!-- HTTPS 스킴만 지원 -->
<data android:scheme="https" />
<!-- 여러 호스트 지원 가능 -->
<data android:host="myapp.example.com" />
<data android:host="www.myapp.example.com" />
<data android:host="m.myapp.example.com" />
<!-- 특정 경로 패턴만 처리하고 싶을 때 -->
<data android:pathPrefix="/app" />
<data android:pathPattern="/share/.*" />
</intent-filter>
<!-- 특정 파일 타입 처리 (선택사항) -->
<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="content" />
<data android:mimeType="application/myapp" />
</intent-filter>
</activity>
</application>
</manifest>
Android 설정 주요 속성 설명:
- android:exported="true": Android 12부터 필수. 다른 앱이 이 액티비티를 시작할 수 있도록 허용
- android:launchMode="singleTop": 이미 실행 중인 앱에서 딥링크를 처리할 때 새 인스턴스를 생성하지 않음
- android:autoVerify="true": App Links를 위한 자동 도메인 확인 활성화
- BROWSABLE 카테고리: 웹 브라우저에서 링크를 열 수 있도록 허용
- android:exported="true": Android 12부터 필수. 다른 앱이 이 액티비티를 시작할 수 있도록 허용
- android:launchMode="singleTop": 이미 실행 중인 앱에서 딥링크를 처리할 때 새 인스턴스를 생성하지 않음
- android:autoVerify="true": App Links를 위한 자동 도메인 확인 활성화
- BROWSABLE 카테고리: 웹 브라우저에서 링크를 열 수 있도록 허용
iOS 플랫폼 설정
iOS에서는 Info.plist 파일과 Associated Domains 설정을 통해 딥링킹을 구현합니다.
xml
<!-- ios/Runner/Info.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 기존 설정들... -->
<!-- URL Schemes 설정 (커스텀 스킴) -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<!-- 스킴 식별자 (역방향 도메인 형식 권장) -->
<key>CFBundleURLName</key>
<string>com.example.myflutterapp.deeplink</string>
<!-- 실제 URL 스킴들 -->
<key>CFBundleURLSchemes</key>
<array>
<string>myflutterapp</string>
<string>myapp</string>
<!-- 여러 스킴 등록 가능 -->
</array>
</dict>
</array>
<!-- iOS 9+ 보안 설정 (필요한 경우) -->
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 앱에서 체크하거나 열 수 있는 다른 앱의 스킴 -->
<string>whatsapp</string>
<string>instagram</string>
<string>fb</string>
</array>
</dict>
</plist>
Universal Links를 위한 추가 설정:
1. Xcode에서 프로젝트 선택
2. Signing & Capabilities 탭 이동
3. "+ Capability" 클릭
4. "Associated Domains" 추가
5. 도메인 추가:
1. Xcode에서 프로젝트 선택
2. Signing & Capabilities 탭 이동
3. "+ Capability" 클릭
4. "Associated Domains" 추가
5. 도메인 추가:
applinks:myapp.example.com
xml
// iOS Runner.entitlements 파일 (자동 생성됨)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<!-- applinks: 접두사는 Universal Links를 의미 -->
<string>applinks:myapp.example.com</string>
<string>applinks:*.myapp.example.com</string>
<!-- 와일드카드 서브도메인 지원 -->
<string>applinks:www.myapp.example.com</string>
</array>
</dict>
</plist>
서버 설정: apple-app-site-association
iOS Universal Links가 작동하려면 웹 서버에 특별한 파일을 호스팅해야 합니다.
json
// https://myapp.example.com/.well-known/apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.example.myflutterapp",
"paths": [
"/app/*",
"/product/*",
"/user/*",
"/share/*",
"NOT /api/*",
"NOT /admin/*"
]
},
{
// 여러 앱 지원 가능
"appID": "TEAM_ID.com.example.myflutterapp.beta",
"paths": ["/beta/*"]
}
]
},
"webcredentials": {
// 자동 로그인 지원 (선택사항)
"apps": ["TEAM_ID.com.example.myflutterapp"]
}
}
서버 설정: assetlinks.json (Android)
json
// https://myapp.example.com/.well-known/assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myflutterapp",
"sha256_cert_fingerprints": [
// Play Console에서 확인 가능한 SHA256 지문
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
}
},
{
// 여러 빌드 변형 지원 (debug, release 등)
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myflutterapp.debug",
"sha256_cert_fingerprints": [
"디버그용_SHA256_지문"
]
}
}
]
Flutter에서 딥링크 테스트하기
설정이 완료되면 다음 명령어로 딥링킹을 테스트할 수 있습니다:
bash
# Android 테스트 (에뮬레이터 또는 연결된 기기에서)
# 커스텀 스킴
adb shell am start -a android.intent.action.VIEW \
-d "myflutterapp://product/123" com.example.myflutterapp
# App Link (HTTPS)
adb shell am start -a android.intent.action.VIEW \
-d "https://myapp.example.com/product/123" com.example.myflutterapp
# iOS 테스트 (시뮬레이터에서)
# 커스텀 스킴
xcrun simctl openurl booted "myflutterapp://product/123"
# Universal Link
xcrun simctl openurl booted "https://myapp.example.com/product/123"
# 실제 기기 테스트
# 1. 메모 앱이나 메시지 앱에 링크 입력
# 2. 링크를 탭하여 앱이 열리는지 확인
# 3. 앱이 설치되어 있지 않은 경우 웹 페이지가 열리는지 확인
일반적인 문제 해결
Universal Links/App Links가 작동하지 않을 때:
1. 도메인 검증 실패
- apple-app-site-association 파일이 HTTPS로 제공되는지 확인
- 파일이 리다이렉션 없이 직접 제공되는지 확인
- Content-Type이 application/json인지 확인
2. 앱이 열리지 않고 웹사이트가 열릴 때
- 사용자가 이전에 "Safari에서 열기"를 선택했을 수 있음
- 설정 > 앱 > 기본 앱에서 확인 및 재설정
3. 특정 경로만 작동하지 않을 때
- paths 배열의 패턴이 올바른지 확인
- NOT 연산자가 우선순위를 가지므로 순서 확인
4. 개발 중 테스트 문제
- 디버그 빌드와 릴리스 빌드의 서명이 다름을 인지
- 로컬 테스트 시 ngrok 같은 터널링 도구 활용
1. 도메인 검증 실패
- apple-app-site-association 파일이 HTTPS로 제공되는지 확인
- 파일이 리다이렉션 없이 직접 제공되는지 확인
- Content-Type이 application/json인지 확인
2. 앱이 열리지 않고 웹사이트가 열릴 때
- 사용자가 이전에 "Safari에서 열기"를 선택했을 수 있음
- 설정 > 앱 > 기본 앱에서 확인 및 재설정
3. 특정 경로만 작동하지 않을 때
- paths 배열의 패턴이 올바른지 확인
- NOT 연산자가 우선순위를 가지므로 순서 확인
4. 개발 중 테스트 문제
- 디버그 빌드와 릴리스 빌드의 서명이 다름을 인지
- 로컬 테스트 시 ngrok 같은 터널링 도구 활용
다음 단계
이제 딥링킹의 기초 개념과 플랫폼별 설정을 완료했습니다. 다음 편에서는 GoRouter를 활용한 고급 라우터 관리 시스템 구축 방법을 다루겠습니다.
시리즈 구성:
- 1편: 기초 개념과 플랫폼 설정 (현재)
- 2편: GoRouter 고급 라우터 관리
- 3편: 딥링크 처리 서비스 구현
- 4편: 라우터 상태 관리와 분석
- 5편: 실전 통합 구현
플랫폼 설정은 한 번만 제대로 해두면 계속 사용할 수 있는 기반 작업입니다. 설정이 올바른지 충분히 테스트한 후 다음 단계로 진행하시기 바랍니다.
시리즈 구성:
- 1편: 기초 개념과 플랫폼 설정 (현재)
- 2편: GoRouter 고급 라우터 관리
- 3편: 딥링크 처리 서비스 구현
- 4편: 라우터 상태 관리와 분석
- 5편: 실전 통합 구현
플랫폼 설정은 한 번만 제대로 해두면 계속 사용할 수 있는 기반 작업입니다. 설정이 올바른지 충분히 테스트한 후 다음 단계로 진행하시기 바랍니다.