앱 삭제 추적
앱 제거를 추적하여 사용자 리텐션을 측정하고 푸시 알림 서비스를 Singular SDK와 연동하여 리인게이지먼트 캠페인을 최적화하세요.
중요: 구글은 2018년 4월에 GCM API를 더 이상 사용하지 않습니다. 모든 안드로이드 앱 제거 추적 구현에는 Firebase 클라우드 메시징(FCM)을 사용하세요.
Android 제거 추적
전제 조건
리액트 네이티브 앱에서 앱 제거 추적을 구현하기 전에 안드로이드 앱 제거 추적 설정 가이드에 따라 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 패키지 설치
핵심 기능 및 메시징 지원을 위해 React Native Firebase 종속성을 추가합니다.
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
2단계: Firebase 구성
Android용 React Native 프로젝트에 Firebase 구성 파일을 추가합니다.
- Firebase 콘솔 프로젝트에 안드로이드 앱을 등록하세요.
-
google-services.json을 다운로드하고android/app/에 배치합니다. - 프로젝트에 Firebase 메시징 종속성이 추가되었는지 확인합니다.
자세한 설정 지침은 React Native Firebase 안드로이드 설정을 참조하세요.
3단계: 알림 권한 요청
FCM 토큰을 검색하기 전에 사용자에게 알림 권한을 요청합니다(Android 13 이상에서 필요).
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
async function requestNotificationPermission() {
if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
// Android 13+ requires explicit permission request
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
// Android 12 and below automatically have permission
return true;
}
// iOS permission handled separately
return true;
}
4단계: FCM 토큰 검색 및 등록하기
권한을 요청한 후 setUninstallToken() 을 사용하여 FCM 디바이스 토큰을 가져와 Singular에 등록합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
);
}
전체 메서드 설명서는 setUninstallToken 참조를 참조하세요.
5단계: 토큰 새로 고침 처리
정확한 제거 추적을 유지하기 위해 새로 고침할 때마다 Singular로 FCM 토큰을 업데이트하세요.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
모범 사례: FCM 토큰은 언제든지 새로 고칠 수 있습니다(앱 업데이트, 디바이스 복원 등). 항상 onTokenRefresh 이벤트를 구독하여 Singular를 최신 토큰으로 업데이트하세요.
iOS 앱 제거 추적
전제 조건
iOS 앱 제거 추적 설정 가이드에 따라 Singular 플랫폼에서 iOS 앱을 구성합니다.
iOS의 앱 제거 추적은 Apple 푸시 알림 서비스(APN) 기술을 기반으로 합니다. 앱이 푸시 알림을 지원하지 않는 경우 Apple의 APN에 앱 등록하기 가이드를 참조하세요.
구현 단계
1단계: iOS 알림 권한 요청
사용자에게 알림 권한을 요청하고 APNS 디바이스 토큰을 검색합니다.
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
async function requestIOSNotificationPermission() {
if (Platform.OS !== 'ios') {
return false;
}
try {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('iOS notification authorization status:', authStatus);
return true;
}
return false;
} catch (error) {
console.error('Error requesting iOS notification permission:', error);
return false;
}
}
2단계: APNS 토큰 검색 및 등록하기
권한이 부여된 후 APNS 디바이스 토큰을 가져와 setUninstallToken() 을 사용하여 Singular에 등록합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
);
}
토큰 형식: getAPNSToken() 에서 검색한 APNS 토큰은 이미 16진수 문자열로 형식이 지정되어 있으며, 이는 Singular에 적합한 형식입니다.
3단계: 토큰 새로 고침 처리하기(iOS)
앱 수명 주기 동안 변경되는 경우 APNS 토큰을 Singular로 업데이트합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
완벽한 크로스 플랫폼 구현
연동 제거 추적 설정
적절한 오류 처리 및 토큰 새로 고침 로직을 통해 Android 및 iOS 플랫폼 모두에 대한 제거 추적을 구현하세요.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
const unsubscribe = setupTokenRefreshListener();
// Clean up listener on unmount
return () => {
if (typeof unsubscribe === 'function') unsubscribe();
};
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
NativeSingular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
NativeSingular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
NativeSingular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
setupTokenRefreshListener();
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
Singular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
Singular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
Singular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
);
}
검증 및 문제 해결
구현 확인
프로덕션에 배포하기 전에 제거 추적이 올바르게 작동하는지 확인하세요.
- 로그를 확인합니다: 콘솔 로그에 토큰 등록이 올바른 형식으로 표시되는지 확인합니다.
- 토큰 생성 테스트: 권한 부여 후 앱을 처음 실행할 때 토큰이 생성되는지 확인합니다.
- 대시보드 모니터링: 대시보드 모니터링: 24-48시간 후 Singular 대시보드에서 제거 추적 데이터를 확인합니다.
- 토큰 새로 고침 테스트: 앱 재실행 시 앱 데이터를 지우고 토큰 업데이트가 올바르게 수행되는지 확인합니다.
일반적인 문제
- 토큰이 생성되지 않음: Firebase 종속성이 올바르게 설치되어 있고 React Native 프로젝트에 Firebase가 구성되어 있는지 확인합니다.
- 권한이 거부되었습니다: 사용자가 알림 권한을 부여했는지 확인합니다(Android 13 이상 및 모든 iOS 버전에 필요).
-
토큰이 업데이트되지 않음: 두 플랫폼 모두에 대해
onTokenRefresh이벤트를 구독했는지 확인하세요. - 데이터 누락: 디바이스가 플랫폼 요구 사항을 충족하는지 확인합니다(Android 4.1+는 Google Play 서비스, iOS는 APN 지원).
- 구성 오류: 앱의 Singular 플랫폼 설정에서 앱 제거 추적이 활성화되어 있는지 확인합니다.
-
파이어베이스 설정: Android의 경우
google-services.json이android/app/에 있는지 확인합니다. iOS의 경우GoogleService-Info.plist이 Xcode 프로젝트에 추가되었는지 확인합니다.
추가 리소스: 자세한 문제 해결 방법은 Android 제거 추적 설정 가이드, iOS 제거 추적 설정 가이드 및 React Native Firebase 메시징 설명서를 참조하세요.