Flutter 애플리케이션에서 딥 링킹 개념은 무엇인가요?

질문

Flutter 애플리케이션에서 딥 링킹(Deep Linking) 개념에 대해 설명해주세요.

답변

딥 링킹은 앱의 특정 콘텐츠나 화면으로 직접 이동할 수 있는 URL 또는 링크를 말합니다. Flutter 애플리케이션에서 딥 링킹을 구현하면 외부 소스(웹사이트, 이메일, 메시지 등)에서 앱의 특정 화면으로 사용자를 직접 안내할 수 있습니다.

딥 링킹의 종류

Flutter에서는 두 가지 유형의 딥 링크를 지원합니다:

  1. URI 스킴 링크: 사용자 정의 스킴을 사용하는 링크 (예: myapp://details/123)
  2. 유니버셜 링크(iOS)/앱 링크(Android): 일반 웹 URL을 사용하는 링크 (예: https://myapp.com/details/123)

딥 링킹의 중요성

딥 링킹이 중요한 이유는 다음과 같습니다:

  1. 향상된 사용자 경험: 사용자가 원하는 콘텐츠로 바로 이동할 수 있습니다.
  2. 마케팅 캠페인 효율성: 특정 프로모션이나 기능으로 사용자를 직접 안내할 수 있습니다.
  3. 앱 간 통합: 다른 앱에서 특정 기능에 접근할 수 있도록 합니다.
  4. 콘텐츠 공유 용이성: 앱 내 특정 콘텐츠를 쉽게 공유할 수 있습니다.
  5. 검색 최적화: 앱 콘텐츠가 검색 엔진에 더 잘 노출될 수 있습니다.

Flutter에서 딥 링킹 구현하기

Flutter 앱에서 딥 링킹을 구현하는 과정은 크게 세 부분으로 나눌 수 있습니다:

  1. 네이티브 플랫폼 설정
  2. Flutter에서 딥 링크 처리
  3. 링크에 따른 내비게이션 구현

1. 네이티브 플랫폼 설정

Android 설정 (AndroidManifest.xml)
<manifest ...>
  <application ...>
    <activity ...>
      <!-- URI 스킴 링크 설정 -->
      <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="details" />
      </intent-filter>

      <!-- 앱 링크 설정 (Android) -->
      <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="myapp.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.myapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

<!-- 유니버셜 링크 설정 (iOS) -->
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:myapp.com</string>
</array>

2. Flutter에서 딥 링크 처리

Flutter에서 딥 링크를 처리하는 두 가지 주요 방법이 있습니다:

import 'package:uni_links/uni_links.dart';
import 'dart:async';
import 'package:flutter/services.dart' show PlatformException;

// 앱 시작 시 초기 링크 처리
Future<void> initUniLinks() async {
  // 초기 URI 처리
  try {
    final initialUri = await getInitialUri();
    // 초기 링크가 있으면 처리
    if (initialUri != null) {
      handleDeepLink(initialUri);
    }
  } on PlatformException {
    // 오류 처리
    print('초기 링크를 가져오는 중 오류가 발생했습니다.');
  }

  // 앱이 실행 중일 때 새 링크 수신 대기
  uriLinkStream.listen((Uri? uri) {
    // 새 링크가 있으면 처리
    if (uri != null) {
      handleDeepLink(uri);
    }
  }, onError: (err) {
    // 오류 처리
    print('URI 스트림 오류: $err');
  });
}

void handleDeepLink(Uri uri) {
  // URI 분석 및 적절한 화면으로 이동
  print('딥 링크 수신: $uri');

  // 경로 및 매개변수 추출
  String path = uri.path;
  Map<String, String> queryParams = uri.queryParameters;

  // 적절한 라우팅 로직 구현
  if (path.startsWith('/details')) {
    String id = path.split('/').last;
    navigateToDetails(id, queryParams);
  } else if (path.startsWith('/profile')) {
    navigateToProfile(queryParams['userId']);
  }
}

더 복잡한 인증 흐름이나 추적 기능이 필요한 경우 이러한 특수 목적 패키지를 사용할 수 있습니다.

3. 링크에 따른 내비게이션 구현

딥 링크를 처리한 후 적절한 화면으로 이동하는 내비게이션 로직을 구현해야 합니다.

// 전역 내비게이터 키를 사용하여 어디서나 내비게이션 접근 가능
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    initUniLinks();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey, // 전역 내비게이터 키 설정
      onGenerateRoute: (settings) {
        // 경로 기반 라우팅 로직
        if (settings.name!.startsWith('/details/')) {
          final id = settings.name!.split('/').last;
          return MaterialPageRoute(
            builder: (context) => DetailsScreen(id: id),
          );
        }
        // 다른 라우트 처리...
        return MaterialPageRoute(builder: (context) => HomeScreen());
      },
      // 기본 라우트
      routes: {
        '/': (context) => HomeScreen(),
        '/profile': (context) => ProfileScreen(),
      },
    );
  }
}

// 딥 링크 처리를 위한 내비게이션 함수
void navigateToDetails(String id, Map<String, String> params) {
  navigatorKey.currentState!.pushNamed('/details/$id');
}

void navigateToProfile(String? userId) {
  if (userId != null) {
    navigatorKey.currentState!.pushNamed('/profile', arguments: {'userId': userId});
  } else {
    navigatorKey.currentState!.pushNamed('/profile');
  }
}

go_router를 사용한 딥 링킹 구현

go_router 패키지는 딥 링크 처리를 더 간편하게 만들어 줍니다:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

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

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
    ),
    GoRoute(
      path: '/details/:id',
      builder: (context, state) {
        final id = state.params['id']!;
        return DetailsScreen(id: id);
      },
    ),
    GoRoute(
      path: '/profile',
      builder: (context, state) {
        final userId = state.queryParams['userId'];
        return ProfileScreen(userId: userId);
      },
    ),
  ],
  // 페이지를 찾을 수 없을 때 리디렉션
  errorBuilder: (context, state) => NotFoundScreen(),
);

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

이 설정으로 myapp://details/123 또는 https://myapp.com/details/123과 같은 링크가 자동으로 적절한 화면으로 라우팅됩니다.

웹 및 Apple 앱 연결 설정

유니버셜 링크(iOS)와 앱 링크(Android)를 완전히 설정하려면 웹 서버 구성도 필요합니다:

Android 앱 링크용 assetlinks.json

웹 서버의 /.well-known/assetlinks.json에 배치:

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

iOS 유니버셜 링크용 apple-app-site-association

웹 서버의 /.well-known/apple-app-site-association에 배치:

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

딥 링크 테스트

1. adb를 사용한 Android 테스트

adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "myapp://details/123"

2. xcrun을 사용한 iOS 테스트

xcrun simctl openurl booted "myapp://details/123"

딥 링킹 모범 사례

  1. 일관된 링크 구조 유지: 웹 URL과 앱 내 라우트 간에 일관된 구조를 유지하세요.
  2. 오류 처리: 잘못된 링크나 파라미터에 대한 우아한 오류 처리를 구현하세요.
  3. 사용자 상태 고려: 로그인이 필요한 화면으로 딥 링크될 때 인증 상태를 확인하세요.
  4. 딥 링크 분석: 어떤 딥 링크가 가장 많이 사용되는지 추적하여 사용자 행동을 이해하세요.
  5. 링크 단축: 공유하기 쉽도록 긴 딥 링크를 단축하는 것을 고려하세요.
  6. 동적 링크 사용: Firebase Dynamic Links와 같은 서비스를 사용하여 보다 강력한 딥 링킹 경험을 제공하세요.

실제 사용 사례

  1. 소셜 미디어 공유: 특정 콘텐츠를 공유할 때 해당 콘텐츠로 직접 이동하는 링크 제공
  2. 마케팅 캠페인: 이메일 또는 SMS 마케팅에서 특정 프로모션 페이지로 이동
  3. 알림: 앱 알림에서 관련 콘텐츠로 바로 이동
  4. QR 코드: 오프라인 마케팅 자료에서 앱의 특정 기능으로 이동
  5. 소셜 로그인 후 리디렉션: 소셜 로그인 후 원래 이용하려던 페이지로 돌아가기

결론

딥 링킹은 Flutter 앱의 사용성과 접근성을 크게 향상시키는 강력한 기능입니다. 외부 소스에서 앱의 특정 콘텐츠로 직접 연결하여 사용자 경험을 원활하게 만들고, 앱 참여도를 높이는 데 도움이 됩니다. 명명된 라우트, 네비게이션 시스템과 함께 사용하면 앱의 내비게이션 구조를 더 강력하고 유연하게 만들 수 있습니다.

Flutter에서 딥 링킹을 구현할 때는 네이티브 플랫폼 설정, 링크 수신 처리, 적절한 내비게이션 로직 구현 등 여러 측면을 고려해야 합니다. uni_links, go_router와 같은 패키지를 사용하면 이 과정을 크게 단순화할 수 있습니다.

results matching ""

    No results matching ""