Flutter 애플리케이션에서 딥 링킹 개념은 무엇인가요?
질문
Flutter 애플리케이션에서 딥 링킹(Deep Linking) 개념에 대해 설명해주세요.
답변
딥 링킹은 앱의 특정 콘텐츠나 화면으로 직접 이동할 수 있는 URL 또는 링크를 말합니다. Flutter 애플리케이션에서 딥 링킹을 구현하면 외부 소스(웹사이트, 이메일, 메시지 등)에서 앱의 특정 화면으로 사용자를 직접 안내할 수 있습니다.
딥 링킹의 종류
Flutter에서는 두 가지 유형의 딥 링크를 지원합니다:
- URI 스킴 링크: 사용자 정의 스킴을 사용하는 링크 (예:
myapp://details/123
) - 유니버셜 링크(iOS)/앱 링크(Android): 일반 웹 URL을 사용하는 링크 (예:
https://myapp.com/details/123
)
딥 링킹의 중요성
딥 링킹이 중요한 이유는 다음과 같습니다:
- 향상된 사용자 경험: 사용자가 원하는 콘텐츠로 바로 이동할 수 있습니다.
- 마케팅 캠페인 효율성: 특정 프로모션이나 기능으로 사용자를 직접 안내할 수 있습니다.
- 앱 간 통합: 다른 앱에서 특정 기능에 접근할 수 있도록 합니다.
- 콘텐츠 공유 용이성: 앱 내 특정 콘텐츠를 쉽게 공유할 수 있습니다.
- 검색 최적화: 앱 콘텐츠가 검색 엔진에 더 잘 노출될 수 있습니다.
Flutter에서 딥 링킹 구현하기
Flutter 앱에서 딥 링킹을 구현하는 과정은 크게 세 부분으로 나눌 수 있습니다:
- 네이티브 플랫폼 설정
- Flutter에서 딥 링크 처리
- 링크에 따른 내비게이션 구현
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에서 딥 링크를 처리하는 두 가지 주요 방법이 있습니다:
a. uni_links 패키지 사용
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']);
}
}
b. flutter_appauth 또는 firebase_dynamic_links 사용
더 복잡한 인증 흐름이나 추적 기능이 필요한 경우 이러한 특수 목적 패키지를 사용할 수 있습니다.
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"
딥 링킹 모범 사례
- 일관된 링크 구조 유지: 웹 URL과 앱 내 라우트 간에 일관된 구조를 유지하세요.
- 오류 처리: 잘못된 링크나 파라미터에 대한 우아한 오류 처리를 구현하세요.
- 사용자 상태 고려: 로그인이 필요한 화면으로 딥 링크될 때 인증 상태를 확인하세요.
- 딥 링크 분석: 어떤 딥 링크가 가장 많이 사용되는지 추적하여 사용자 행동을 이해하세요.
- 링크 단축: 공유하기 쉽도록 긴 딥 링크를 단축하는 것을 고려하세요.
- 동적 링크 사용: Firebase Dynamic Links와 같은 서비스를 사용하여 보다 강력한 딥 링킹 경험을 제공하세요.
실제 사용 사례
- 소셜 미디어 공유: 특정 콘텐츠를 공유할 때 해당 콘텐츠로 직접 이동하는 링크 제공
- 마케팅 캠페인: 이메일 또는 SMS 마케팅에서 특정 프로모션 페이지로 이동
- 알림: 앱 알림에서 관련 콘텐츠로 바로 이동
- QR 코드: 오프라인 마케팅 자료에서 앱의 특정 기능으로 이동
- 소셜 로그인 후 리디렉션: 소셜 로그인 후 원래 이용하려던 페이지로 돌아가기
결론
딥 링킹은 Flutter 앱의 사용성과 접근성을 크게 향상시키는 강력한 기능입니다. 외부 소스에서 앱의 특정 콘텐츠로 직접 연결하여 사용자 경험을 원활하게 만들고, 앱 참여도를 높이는 데 도움이 됩니다. 명명된 라우트, 네비게이션 시스템과 함께 사용하면 앱의 내비게이션 구조를 더 강력하고 유연하게 만들 수 있습니다.
Flutter에서 딥 링킹을 구현할 때는 네이티브 플랫폼 설정, 링크 수신 처리, 적절한 내비게이션 로직 구현 등 여러 측면을 고려해야 합니다. uni_links
, go_router
와 같은 패키지를 사용하면 이 과정을 크게 단순화할 수 있습니다.