Flutter 개발

Flutter 딥링킹 완벽 가이드 1편: 기초 개념과 플랫폼 설정

딥링킹의 개념부터 Android/iOS 플랫폼별 상세 설정까지

2025년 9월 20일
10분 읽기
Flutter 딥링킹 완벽 가이드 1편: 기초 개념과 플랫폼 설정

딥링킹이란 무엇인가

딥링킹(Deep Linking)은 사용자를 앱의 특정 화면이나 콘텐츠로 직접 이동시키는 기술입니다. 웹의 URL과 유사한 개념으로, 모바일 앱에서도 특정 리소스에 직접 접근할 수 있게 해줍니다.

딥링킹이 필요한 실제 상황:
- 푸시 알림: "새 메시지가 도착했습니다" 알림을 탭하면 해당 채팅방으로 직접 이동
- 이메일 마케팅: 프로모션 이메일의 링크를 통해 앱 내 특정 상품 페이지로 이동
- 소셜 미디어 공유: Instagram에 공유된 링크로 앱의 특정 게시물 열기
- QR 코드: 오프라인 매장의 QR 코드로 앱 내 쿠폰 페이지 접근
- 앱 간 연동: 카카오톡에서 배달 앱의 특정 메뉴로 바로 이동

딥링킹의 세 가지 유형

딥링킹은 구현 방식과 동작 원리에 따라 세 가지 주요 유형으로 분류됩니다.

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 카테고리: 웹 브라우저에서 링크를 열 수 있도록 허용

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. 도메인 추가: 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 같은 터널링 도구 활용

다음 단계

이제 딥링킹의 기초 개념과 플랫폼별 설정을 완료했습니다. 다음 편에서는 GoRouter를 활용한 고급 라우터 관리 시스템 구축 방법을 다루겠습니다.

시리즈 구성:
- 1편: 기초 개념과 플랫폼 설정 (현재)
- 2편: GoRouter 고급 라우터 관리
- 3편: 딥링크 처리 서비스 구현
- 4편: 라우터 상태 관리와 분석
- 5편: 실전 통합 구현

플랫폼 설정은 한 번만 제대로 해두면 계속 사용할 수 있는 기반 작업입니다. 설정이 올바른지 충분히 테스트한 후 다음 단계로 진행하시기 바랍니다.
#딥링킹
#URL스킴
#유니버설링크
#앱링크
#Android
#iOS
#플랫폼설정