앱 삭제 추적
앱 제거를 추적하여 사용자 리텐션을 측정하고 푸시 알림 서비스를 Singular SDK와 연동하여 리인게이지먼트 캠페인을 최적화하세요.
중요: 구글은 2018년 4월에 GCM API를 더 이상 사용하지 않습니다. 모든 안드로이드 앱 제거 추적 구현에는 Firebase 클라우드 메시징(FCM)을 사용하세요.
Android 제거 추적
전제 조건
Flutter 앱에서 앱 제거 추적을 구현하기 전에 안드로이드 앱 제거 추적 설정 가이드에 따라 Singular 플랫폼에서 앱을 구성하세요.
시스템 요구 사항
앱 제거 추적을 사용하려면 Firebase 클라우드 메시징 및 특정 기기 구성이 필요합니다.
FCM 요구 사항(소스):
- Android 버전: 기기는 Android 4.1(API 16) 이상을 실행해야 합니다.
- Google Play 서비스: 기기에 Google Play 스토어 앱이 설치되어 있어야 합니다.
- 에뮬레이터 지원: Google API가 포함된 Android 4.1 이상 에뮬레이터가 지원됩니다.
- 배포: 앱 제거 추적을 지원하면서 Google Play 스토어 외부에 앱을 배포할 수 있습니다.
참고: 지원되지 않는 Android 버전 또는 Google Play 서비스가 없는 기기를 사용하는 사용자는 제거 추적이 되지 않습니다.
구현 단계
1단계: Firebase 패키지 설치
핵심 기능 및 메시징 지원을 위해 pubspec.yaml 파일에 Firebase 종속성을 추가합니다.
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2
firebase_messaging: ^14.7.10
singular_flutter_sdk: ^1.8.0
종속성을 추가한 후 flutter pub get 을 실행하여 패키지를 설치합니다.
2단계: Firebase 구성
Android용 Flutter 프로젝트에 Firebase 구성 파일을 추가합니다.
- Firebase 콘솔 프로젝트에 Android 앱을 등록합니다.
-
google-services.json을 다운로드하여android/app/에 배치합니다. - 프로젝트에 Firebase 메시징 종속성이 추가되었는지 확인합니다.
자세한 설정 지침은 Flutter 앱에 Firebase 추가를 참조하십시오.
3단계: Firebase 초기화
메시징 서비스를 활성화하려면 Flutter 앱을 실행하기 전에 Firebase를 초기화하세요.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
4단계: 알림 권한 요청
FCM 토큰을 가져오기 전에 사용자에게 알림 권한을 요청합니다(Android 13 이상에서 필요).
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
Future<bool> requestNotificationPermission() async {
if (Platform.isAndroid) {
// Android 13+ requires explicit permission request
// Note: firebase_messaging handles this automatically via requestPermission
final FirebaseMessaging messaging = FirebaseMessaging.instance;
final NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
// iOS permission handled separately
return true;
}
5단계: FCM 토큰 검색 및 등록하기
권한을 요청한 후 registerDeviceTokenForUninstall() 을 사용하여 FCM 디바이스 토큰을 가져와 Singular에 등록합니다.
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
initializeAndroidUninstallTracking();
}
}
Future<void> initializeAndroidUninstallTracking() async {
try {
// Request notification permission
final hasPermission = await requestNotificationPermission();
if (!hasPermission) {
print('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
final token = await FirebaseMessaging.instance.getToken();
if (token != null) {
// Register token with Singular for uninstall tracking
Singular.registerDeviceTokenForUninstall(token);
print('FCM token registered with Singular: $token');
} else {
print('No FCM token available');
}
} catch (error) {
print('Error setting up uninstall tracking: $error');
}
}
Future<bool> requestNotificationPermission() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
메소드 서명:
static void registerDeviceTokenForUninstall(String token)
전체 메서드 설명서는 registerDeviceTokenForUninstall 참조를 참조하세요.
6단계: 토큰 새로 고침 처리
정확한 제거 추적을 유지하기 위해 새로 고침할 때마다 Singular로 FCM 토큰을 업데이트하세요.
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
setupTokenRefreshListener();
}
}
void setupTokenRefreshListener() {
// Listen for token refresh events
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('FCM token refreshed: $token');
// Update Singular with new token
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
모범 사례: FCM 토큰은 언제든지 새로 고칠 수 있습니다(앱 업데이트, 디바이스 복원 등). 항상 onTokenRefresh 스트림을 구독하여 Singular를 최신 토큰으로 업데이트하세요.
iOS 앱 제거 추적
전제 조건
iOS 앱 제거 추적 설정 가이드에 따라 Singular 플랫폼에서 iOS 앱을 구성합니다.
iOS의 앱 제거 추적은 Apple 푸시 알림 서비스(APN) 기술을 기반으로 합니다. 앱이 푸시 알림을 지원하지 않는 경우 Apple의 APN에 앱 등록하기 가이드를 참조하세요.
구현 단계
1단계: iOS 프로젝트 구성
iOS 프로젝트에서 Firebase 구성을 추가하고 푸시 알림 기능을 사용 설정합니다.
- Firebase 콘솔 프로젝트에 iOS 앱 등록하기
-
GoogleService-Info.plist을 다운로드하여 Xcode Runner 폴더에 추가합니다. - Xcode 프로젝트 설정에서 푸시 알림 기능을 활성화합니다.
- 백그라운드 모드를 활성화하고 원격 알림을 확인합니다.
2단계: iOS 알림 권한 요청
사용자에게 알림 권한을 요청하고 APNS 디바이스 토큰을 검색합니다.
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
Future<bool> requestIOSNotificationPermission() async {
if (!Platform.isIOS) {
return false;
}
try {
final FirebaseMessaging messaging = FirebaseMessaging.instance;
final NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
final authorized =
settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
if (authorized) {
print('iOS notification authorization status: ${settings.authorizationStatus}');
return true;
}
return false;
} catch (error) {
print('Error requesting iOS notification permission: $error');
return false;
}
}
3단계: APNS 토큰 검색 및 등록하기
권한이 부여된 후 APNS 디바이스 토큰을 가져와 registerDeviceTokenForUninstall() 을 사용하여 Singular에 등록합니다.
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isIOS) {
initializeIOSUninstallTracking();
}
}
Future<void> initializeIOSUninstallTracking() async {
try {
// Request notification authorization
final hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
print('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get APNS token
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
// Register token with Singular for uninstall tracking
Singular.registerDeviceTokenForUninstall(apnsToken);
print('APNS token registered with Singular: $apnsToken');
} else {
print('No APNS token available');
}
} catch (error) {
print('Error setting up iOS uninstall tracking: $error');
}
}
Future<bool> requestIOSNotificationPermission() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
토큰 형식: getAPNSToken() 에서 검색한 APNS 토큰은 이미 16진수 문자열로 형식이 지정되어 있으며, 이는 Singular에 적합한 형식입니다.
4단계: 토큰 새로 고침 처리하기(iOS)
앱 수명 주기 동안 변경되는 경우 APNS 토큰을 Singular로 업데이트합니다.
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isIOS) {
setupIOSTokenRefreshListener();
}
}
void setupIOSTokenRefreshListener() {
// Listen for token refresh events
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('APNS token refreshed: $token');
// Update Singular with new token
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
완벽한 크로스 플랫폼 구현
연동 제거 추적 설정
적절한 오류 처리 및 토큰 새로 고침 로직을 사용하여 Android 및 iOS 플랫폼 모두에 대한 제거 추적을 구현하세요.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'dart:io';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
// Initialize Singular SDK
initializeSingularSDK();
// Setup uninstall tracking
initializeUninstallTracking();
// Setup token refresh listener
setupTokenRefreshListener();
}
void initializeSingularSDK() {
final config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
Singular.start(config);
}
Future<void> initializeUninstallTracking() async {
try {
if (Platform.isAndroid) {
await setupAndroidUninstallTracking();
} else if (Platform.isIOS) {
await setupIOSUninstallTracking();
}
} catch (error) {
print('Error initializing uninstall tracking: $error');
}
}
Future<void> setupAndroidUninstallTracking() async {
print('Setting up Android uninstall tracking');
// Request notification permission
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus != AuthorizationStatus.authorized &&
settings.authorizationStatus != AuthorizationStatus.provisional) {
print('Android notification permission denied');
return;
}
// Get and register FCM token
final token = await messaging.getToken();
if (token != null) {
Singular.registerDeviceTokenForUninstall(token);
print('Android FCM token registered: $token');
} else {
print('Failed to retrieve Android FCM token');
}
}
Future<void> setupIOSUninstallTracking() async {
print('Setting up iOS uninstall tracking');
// Request iOS notification authorization
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
final authorized =
settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
if (!authorized) {
print('iOS notification permission denied');
return;
}
// Get and register APNS token
final apnsToken = await messaging.getAPNSToken();
if (apnsToken != null) {
Singular.registerDeviceTokenForUninstall(apnsToken);
print('iOS APNS token registered: $apnsToken');
} else {
print('Failed to retrieve iOS APNS token');
}
}
void setupTokenRefreshListener() {
// Listen for token refresh events (works for both platforms)
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('${Platform.operatingSystem.toUpperCase()} token refreshed: $token');
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Uninstall Tracking Demo'),
),
body: Center(
child: Text('Uninstall tracking initialized'),
),
),
);
}
}
플랫폼별 참고 사항
- iOS: 앱에 필요한 푸시 알림 권한이 있는지 확인하고 Apple 개발자 계정에서 APN이 올바르게 구성되었는지 확인합니다.
-
Android: Firebase 콘솔에서 FCM이 설정되어 있고 프로젝트에
google-services.json파일이android/app/에 포함되어 있는지 확인합니다.
확인 및 문제 해결
구현 확인
프로덕션에 배포하기 전에 제거 추적이 올바르게 작동하는지 확인하세요.
- 로그를 확인합니다: 콘솔 로그에 토큰 등록이 올바른 형식으로 표시되는지 확인합니다.
- 토큰 생성 테스트: 권한 부여 후 앱을 처음 실행할 때 토큰이 생성되는지 확인합니다.
- 대시보드 모니터링: 대시보드 모니터링: 24-48시간 후 Singular 대시보드에서 제거 추적 데이터를 확인합니다.
- 토큰 새로 고침 테스트: 앱 재실행 시 앱 데이터를 지우고 토큰 업데이트가 올바르게 수행되는지 확인합니다.
일반적인 문제
-
토큰이 생성되지 않음: Firebase 종속성이 올바르게 설치되어 있고 Flutter 프로젝트에 Firebase가 구성되어 있는지 확인합니다. 종속성을 추가한 후
flutter pub get을 실행합니다. - 권한이 거부되었습니다: 사용자에게 알림 권한을 부여했는지 확인합니다. Android 13 이상의 경우 명시적인 권한 요청이 필요합니다. iOS의 경우 사용자가 알림을 승인해야 합니다.
-
토큰이 업데이트되지 않습니다: 두 플랫폼 모두
onTokenRefresh스트림을 구독하고 있는지 확인하세요. 앱 초기화에서 수신기를 설정해야 합니다. - 누락된 데이터: 디바이스가 플랫폼 요구 사항을 충족하는지 확인합니다(Android 4.1+의 경우 Google Play 서비스, iOS의 경우 APNs 지원). 이러한 서비스가 없는 디바이스는 추적할 수 없습니다.
- 구성 오류: 앱의 Singular 플랫폼 설정에서 앱 삭제 추적이 활성화되어 있는지 확인합니다. 사전 요구 사항에 링크된 플랫폼별 설정 가이드를 따르세요.
-
파이어베이스 설정: Android의 경우
google-services.json이android/app/에 있는지 확인합니다. iOS의 경우GoogleService-Info.plist이 Xcode 프로젝트 런처 폴더에 추가되었는지 확인합니다. -
SDK 초기화:
registerDeviceTokenForUninstall()을 호출하기 전에 Singular SDK 가 초기화되었는지 확인합니다. 토큰 등록은 SDK 시작 후에 이루어져야 합니다. -
플랫폼 감지:
dart:io에서Platform.isAndroid및Platform.isIOS을 사용하여 플랫폼별 코드가 올바른 플랫폼에서 실행되는지 확인합니다.
추가 리소스: 자세한 문제 해결 방법은 Android 제거 추적 설정 가이드, iOS 제거 추적 설정 가이드 및 Flutter 파이어베이스 설정 설명서를 참조하십시오.
모범 사례
토큰 관리
- 조기 등록: 앱 라이프사이클 초기에 가능한 한 빨리 권한을 요청하고 토큰을 등록하세요(첫 번째 앱 실행 시).
- 오류 처리: 토큰 검색 및 등록과 관련된 강력한 오류 처리를 구현하여 장애를 원활하게 처리합니다.
- 토큰 새로 고침: 토큰이 변경될 때 항상 토큰 새로 고침 리스너를 구현하여 Singular를 계속 업데이트하세요.
- 사용자 경험: 상황에 맞게 알림 권한을 요청하고 앱에 필요한 이유를 설명하여 권한 부여율을 개선합니다.
테스트 전략
- 개발 테스트: 에뮬레이터는 푸시 알림 지원이 제한적일 수 있으므로 실제 Android 및 iOS 기기에서 테스트하세요.
- 권한 흐름: 사용자가 권한을 거부하는 시나리오를 테스트하고 앱이 이를 정상적으로 처리하는지 확인합니다.
- 토큰 지속성: 토큰이 앱 재시작 시에도 지속되고 새로고침 시 올바르게 업데이트되는지 확인합니다.
- 로깅: 개발 중에 상세 로깅을 활성화하여 토큰 생성 및 등록을 추적합니다.