Flutter에서 딥 링크(Deep Link)를 어떻게 구현하나요?

질문

Flutter 앱에서 딥 링크를 구현하는 방법을 설명해주세요.

답변

Flutter에서 딥 링크(Deep Link)는 앱의 특정 콘텐츠로 직접 이동할 수 있는 URL을 의미합니다. 이는 웹 URL, QR 코드, 푸시 알림 등에서 앱의 특정 화면으로 바로 이동하는 기능을 가능하게 합니다. Flutter에서는 여러 방법으로 딥 링크를 구현할 수 있습니다.

1. 기본 딥 링크 구현 과정

딥 링크를 구현하기 위한 기본적인 단계는 다음과 같습니다:

1.1 플랫폼별 설정

Android 설정 (AndroidManifest.xml)
<manifest ...>
  <application ...>
    <activity ...>
      <!-- 딥 링크 인텐트 필터 추가 -->
      <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"
          android:host="open" />
      </intent-filter>
      <!-- HTTP/HTTPS 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="https"
          android:host="myapp.example.com" />
      </intent-filter>
    </activity>
  </application>
</manifest>
iOS 설정 (Info.plist)
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>com.example.myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>
<!-- Universal Links를 위한 설정 -->
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:myapp.example.com</string>
</array>

iOS에서 Universal Links를 위해서는 추가로 apple-app-site-association 파일을 웹서버에 호스팅해야 합니다.

1.2 Flutter에서 딥 링크 처리

가장 쉬운 방법은 uni_links 패키지를 사용하는 것입니다:

// pubspec.yaml
dependencies:
  uni_links: ^0.5.1

// 코드 구현
import 'package:uni_links/uni_links.dart';
import 'dart:async';

class _MyAppState extends State<MyApp> {
  StreamSubscription? _deepLinkSubscription;

  @override
  void initState() {
    super.initState();
    initDeepLinks();
  }

  Future<void> initDeepLinks() async {
    // 앱이 이미 실행 중일 때 딥 링크 처리
    _deepLinkSubscription = linkStream.listen((String? link) {
      if (link != null) {
        handleDeepLink(link);
      }
    }, onError: (err) {
      print('딥 링크 오류: $err');
    });

    // 앱이 종료된 상태에서 딥 링크로 시작된 경우 처리
    try {
      final initialLink = await getInitialLink();
      if (initialLink != null) {
        handleDeepLink(initialLink);
      }
    } catch (e) {
      print('초기 딥 링크 오류: $e');
    }
  }

  void handleDeepLink(String link) {
    print('딥 링크 수신: $link');

    // URL 파싱 및 처리 로직
    Uri uri = Uri.parse(link);

    // 예: myapp://open/product/123 처리
    if (uri.host == 'open') {
      final pathSegments = uri.pathSegments;
      if (pathSegments.length >= 2 && pathSegments[0] == 'product') {
        final productId = pathSegments[1];
        navigateToProductDetails(productId);
      }
    }
  }

  void navigateToProductDetails(String productId) {
    // 해당 제품 상세 페이지로 네비게이션
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => ProductDetailScreen(productId: productId),
      ),
    );
  }

  @override
  void dispose() {
    _deepLinkSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ...앱 구성
    );
  }
}

2. 고급 라우팅을 통한 딥 링크 구현

더 복잡한 앱에서는 라우팅 시스템과 딥 링크를 통합하는 것이 좋습니다:

2.1 go_router 패키지 사용

go_router는 URL 기반 라우팅을 제공하여 딥 링크 처리에 적합합니다:

// pubspec.yaml
dependencies:
  go_router: ^10.1.2

// 코드 구현
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
    ),
    GoRoute(
      path: '/product/:id',
      builder: (context, state) {
        final productId = state.pathParameters['id']!;
        return ProductDetailScreen(productId: productId);
      },
    ),
    GoRoute(
      path: '/category/:name',
      builder: (context, state) {
        final categoryName = state.pathParameters['name']!;
        return CategoryScreen(category: categoryName);
      },
    ),
  ],
  redirect: (context, state) {
    // 필요한 경우 리다이렉션 로직 추가
    return null; // 리다이렉션 없음
  },
);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      title: '딥 링크 데모',
    );
  }
}

이제 myapp://open/product/123 또는 https://myapp.example.com/product/123과 같은 딥 링크는 자동으로 상품 상세 페이지로 라우팅됩니다.

3. 파이어베이스 다이나믹 링크 사용하기

Firebase Dynamic Links는 앱 설치 여부에 관계없이 사용자를 올바른 위치로 안내하는 강력한 딥 링크 솔루션입니다:

// pubspec.yaml
dependencies:
  firebase_core: ^2.15.1
  firebase_dynamic_links: ^5.3.5

// 코드 구현
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';

Future<void> initDynamicLinks() async {
  // 앱이 종료된 상태에서 다이나믹 링크로 시작된 경우
  final PendingDynamicLinkData? initialLink = await FirebaseDynamicLinks.instance.getInitialLink();

  if (initialLink != null) {
    final Uri deepLink = initialLink.link;
    handleDynamicLink(deepLink);
  }

  // 앱이 포그라운드/백그라운드 상태일 때 다이나믹 링크 처리
  FirebaseDynamicLinks.instance.onLink.listen(
    (dynamicLinkData) {
      final Uri deepLink = dynamicLinkData.link;
      handleDynamicLink(deepLink);
    },
    onError: (error) {
      print('다이나믹 링크 오류: $error');
    }
  );
}

void handleDynamicLink(Uri deepLink) {
  // URI 파라미터에서 필요한 정보 추출
  final pathSegments = deepLink.pathSegments;
  final queryParams = deepLink.queryParameters;

  if (pathSegments.isNotEmpty) {
    if (pathSegments[0] == 'product' && pathSegments.length > 1) {
      final productId = pathSegments[1];
      // 상품 상세 페이지로 이동
      navigateToProductDetail(productId);
    } else if (pathSegments[0] == 'promotion' && queryParams.containsKey('code')) {
      final promoCode = queryParams['code'];
      // 프로모션 페이지로 이동
      navigateToPromotion(promoCode!);
    }
  }
}

3.1 다이나믹 링크 생성하기

Future<Uri> createDynamicLink(String productId) async {
  final DynamicLinkParameters parameters = DynamicLinkParameters(
    uriPrefix: 'https://myapp.page.link',
    link: Uri.parse('https://myapp.example.com/product/$productId'),
    androidParameters: AndroidParameters(
      packageName: 'com.example.myapp',
      minimumVersion: 1,
    ),
    iosParameters: IOSParameters(
      bundleId: 'com.example.myapp',
      minimumVersion: '1.0.0',
      appStoreId: '123456789',
    ),
    socialMetaTagParameters: SocialMetaTagParameters(
      title: '제품 상세 보기',
      description: '멋진 제품을 확인해보세요!',
      imageUrl: Uri.parse('https://example.com/images/product.jpg'),
    ),
  );

  final shortLink = await FirebaseDynamicLinks.instance.buildShortLink(parameters);
  return shortLink.shortUrl;
}

4. 딥 링크 테스트하기

4.1 adb 명령어로 테스트 (Android)

# 커스텀 스킴 테스트
adb shell am start -a android.intent.action.VIEW -d "myapp://open/product/123" com.example.myapp

# HTTP 링크 테스트
adb shell am start -a android.intent.action.VIEW -d "https://myapp.example.com/product/123" com.example.myapp

4.2 iOS 시뮬레이터에서 테스트

  1. 시뮬레이터 실행
  2. Safari 열기
  3. URL 입력: myapp://open/product/123

보안이 강화된 딥 링크 구현을 위해 Android의 App Links와 iOS의 Universal Links를 설정할 수 있습니다.

assetlinks.json 파일을 웹사이트의 .well-known 디렉토리에 호스팅해야 합니다:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.myapp",
      "sha256_cert_fingerprints": ["SHA256 인증서 지문"]
    }
  }
]

apple-app-site-association 파일을 웹사이트의 .well-known 디렉토리에 호스팅해야 합니다:

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "팀ID.com.example.myapp",
        "paths": ["*"]
      }
    ]
  }
}

6. 딥 링크 처리 시 고려사항

  1. 사용자 경험: 딥 링크로 들어온 사용자가 백 버튼을 눌렀을 때 적절한 화면으로 이동하도록 네비게이션 스택을 관리해야 합니다.

  2. 인증 처리: 로그인이 필요한 화면으로 딥 링크를 통해 들어온 경우, 로그인 상태를 확인하고 필요시 로그인 화면으로 리다이렉션해야 합니다.

  3. 오류 처리: 잘못된 형식의 딥 링크나 존재하지 않는 콘텐츠에 대한 요청을 적절히 처리해야 합니다.

  4. 딥 링크 분석: 딥 링크를 통한 유입 경로를 분석하여 마케팅 캠페인의 효과를 측정할 수 있습니다.

  5. 앱 설치 전환: 앱이 설치되지 않은 경우 스토어로 안내하는 로직을 구현해야 합니다(Firebase Dynamic Links 사용 시 자동 처리).

요약

Flutter에서 딥 링크를 구현하는 방법은 다음과 같이 요약됩니다:

  1. 플랫폼 설정: Android와 iOS 각각의 설정 파일에 딥 링크 스킴과 호스트를 등록합니다.

  2. 링크 처리: uni_links 패키지 또는 라우팅 라이브러리(go_router 등)를 사용하여 링크를 처리합니다.

  3. 고급 기능: Firebase Dynamic Links를 사용하여 설치 유도 및 복잡한 딥 링크 시나리오를 처리할 수 있습니다.

  4. 보안 강화: App Links와 Universal Links를 구현하여 보다 안전하고 신뢰할 수 있는 딥 링크 경험을 제공합니다.

딥 링크는 사용자에게 원활한 앱 경험을 제공하고 마케팅 효과를 높이는 중요한 기능입니다. 적절한 딥 링크 구현은 앱의 사용성과 참여도를 크게 향상시킬 수 있습니다.

results matching ""

    No results matching ""