푸시 알림 지원
푸시 알림을 통해 사용자 상호작용을 추적하여 리인게이지먼트 캠페인과 어트리뷰션 전환을 정확하게 측정할 수 있도록 Firebase 클라우드 메시징(FCM)을 Singular SDK와 연동하세요.
아래 구현 가이드라인에 따라 알림 데이터가 적절한 어트리뷰션을 위해 Singular SDK에 올바르게 전달되도록 하세요.
푸시 알림을 추적해야 하는 이유: 푸시 알림은 리인게이지먼트를 유도하지만 추적을 위해서는 올바른 연동이 필요합니다. Singular는 알림과 상호작용하는 사용자의 어트리뷰션을 올바르게 추적하여 마케팅 캠페인과 참여 전략을 최적화합니다.
구현 가이드
Singular SDK 연동
Singular React Native SDK 가이드에 설명된 대로 표준 설치 지침에 따라 Singular SDK를 React Native 프로젝트에 연동하세요.
Firebase 클라우드 메시징 설정
Firebase 패키지를 설치하고 푸시 알림 지원을 위한 플랫폼별 설정을 구성합니다.
Firebase 패키지 설치
핵심 기능 및 메시징 지원을 위해 React Native Firebase 종속성을 추가합니다.
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
iOS 구성
Firebase에 iOS 앱을 등록하고 Xcode에서 푸시 알림 기능을 구성하세요.
- iOS 앱 등록하기: Firebase 콘솔 프로젝트에서 iOS 앱을 만듭니다.
-
구성 파일 추가:
GoogleService-Info.plist을 다운로드하여 Xcode 프로젝트에 추가합니다. - 기능 활성화: 기능 활성화: Xcode 프로젝트 설정에서 푸시 알림 기능을 활성화합니다.
- 백그라운드 모드를 활성화합니다: 백그라운드 모드 활성화: 백그라운드 모드를 활성화하고 원격 알림을 확인합니다.
Android 구성
Firebase에 Android 앱을 등록하고 프로젝트에 구성 파일을 추가합니다.
- Android 앱 등록하기: Firebase 콘솔 프로젝트에서 Android 앱을 만듭니다.
-
구성 파일 추가:
google-services.json을 다운로드하여android/app/에 배치합니다. - 종속성 확인: Firebase 메시징 종속성이 추가되고 권한이 부여되었는지 확인합니다.
푸시 링크 경로 구성
푸시 알림 페이로드 구조 내에서 Singular 추적 링크가 위치하는 JSON 경로를 정의합니다.
알림 데이터 구조에서 Singular 링크의 키 경로를 지정하는 문자열 배열을 전달하여 푸시 링크 경로를 구성합니다. 각 경로는 키의 중첩 구조를 나타내는 배열입니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
pushNotificationsLinkPaths: [
['sng_link'], // Top-level key
['path', 'to', 'url'], // Nested path
['rootObj', 'nestedObj', 'singularLink'] // Deep nested path
]
};
NativeSingular.init(config);
import { Singular, SingularConfig } from 'singular-react-native';
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withPushNotificationsLinkPaths([
['sng_link'], // Top-level key
['path', 'to', 'url'], // Nested path
['rootObj', 'nestedObj', 'singularLink'] // Deep nested path
]);
Singular.init(config);
경로 구성 예시:
-
단순 키: 페이로드의 최상위 키에
['sng_link']사용 -
중첩된 키: 중첩된 JSON 구조를 횡단하려면
['rootObj', 'nestedObj', 'key']사용 - 다중 경로: 여러 경로 배열을 정의하여 Singular 링크에 대해 가능한 여러 위치를 확인합니다.
전체 메서드 문서에 대한 자세한 내용은 withPushNotificationsLinkPaths 참조를 참조하세요.
플랫폼별 처리
iOS 푸시 알림 처리
종료된 상태의 앱
앱이 종료된 상태에서 열릴 때 자동 푸시 추적을 위해 실행 옵션을 Singular SDK에 전달하도록 iOS 앱디렉티브를 구성하세요.
AppDelegate 에 didFinishLaunchingWithOptions 에 다음을 추가합니다:
// Import at the top of the file
import Singular
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory
window = UIWindow(frame: UIScreen.main.bounds)
if let singularBridge = NSClassFromString("SingularBridge") {
let selector = NSSelectorFromString("startSessionWithLaunchOptions:")
if singularBridge.responds(to: selector) {
singularBridge.perform(selector, with: launchOptions, afterDelay: 1)
}
}
factory.startReactNative(
withModuleName: "YourAppName", // Update with your App Name
in: window,
launchOptions: launchOptions
)
return true
}
// Import at the top of the file
#import <Singular-React-Native/SingularBridge.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Pass launch options to Singular for push tracking
[SingularBridge startSessionWithLaunchOptions:launchOptions];
// Your other initialization code
return YES;
}
자동 처리: 앱이 실행되지 않은 상태에서 사용자가 푸시 알림을 탭하면, 실행 옵션을 통해 앱 실행 중에 Singular가 자동으로 알림 페이로드를 캡처합니다.
백그라운드 또는 포그라운드에서 앱 실행
앱이 백그라운드 또는 포그라운드 상태에서 알림을 수신할 때 handlePushNotification() 메서드를 사용하여 푸시 데이터를 SDK에 전달합니다.
// 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';
export default function App() {
useEffect(() => {
// Handle foreground messages
const unsubscribeBackground = messaging().onMessage(handleForegroundMessage);
// Handle messages that opened the app from background
messaging().onNotificationOpenedApp(handleBackgroundMessage);
return () => {
unsubscribeBackground();
};
}, []);
function handleForegroundMessage(remoteMessage) {
console.log('Foreground message received:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
NativeSingular.handlePushNotification(remoteMessage.data);
}
// Your custom notification handling
displayLocalNotification(remoteMessage);
}
function handleBackgroundMessage(remoteMessage) {
console.log('Background message opened app:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
NativeSingular.handlePushNotification(remoteMessage.data);
}
// Navigate to appropriate screen
navigateFromNotification(remoteMessage);
}
function displayLocalNotification(remoteMessage) {
// Your notification display logic
console.log('Displaying notification:', remoteMessage.notification?.title);
}
function navigateFromNotification(remoteMessage) {
// Your navigation logic based on notification data
const route = remoteMessage.data?.route;
console.log('Navigating to:', route);
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
export default function App() {
useEffect(() => {
// Handle background messages
const unsubscribeBackground = messaging().onMessage(handleForegroundMessage);
// Handle messages that opened the app from background
messaging().onNotificationOpenedApp(handleBackgroundMessage);
return () => {
unsubscribeBackground();
};
}, []);
function handleForegroundMessage(remoteMessage) {
console.log('Foreground message received:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
Singular.handlePushNotification(remoteMessage.data);
}
// Your custom notification handling
displayLocalNotification(remoteMessage);
}
function handleBackgroundMessage(remoteMessage) {
console.log('Background message opened app:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
Singular.handlePushNotification(remoteMessage.data);
}
// Navigate to appropriate screen
navigateFromNotification(remoteMessage);
}
function displayLocalNotification(remoteMessage) {
// Your notification display logic
console.log('Displaying notification:', remoteMessage.notification?.title);
}
function navigateFromNotification(remoteMessage) {
// Your navigation logic based on notification data
const route = remoteMessage.data?.route;
console.log('Navigating to:', route);
}
return (
// Your app components
);
}
전체 메서드 문서는 handlePushNotification 참조를 참조하세요.
안드로이드 푸시 알림 처리
종료된 상태의 앱
종료된 상태의 안드로이드 앱에는 아무런 조치가 필요하지 않습니다. 사용자가 알림을 탭하면 React Native 브릿지 레이어가 이 시나리오를 자동으로 처리합니다.
자동 처리: 앱이 실행 중이 아닌 상태에서 사용자가 푸시 알림을 탭하면 Singular는 네이티브 브리지 연동을 통해 알림 데이터를 자동으로 캡처합니다.
백그라운드 또는 포그라운드의 앱
앱이 백그라운드 또는 포그라운드 상태일 때 알림 인텐트를 Singular SDK에 전달하도록 안드로이드 메인 액티비티를 구성하세요.
메인 액티비티(예: MainActivity.java 또는 MainActivity.kt)에서 onNewIntent 을 재정의합니다:
// Add imports at the top
import android.content.Intent;
import net.singular.react_native.SingularBridgeModule;
// Add to MainActivity class
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// Pass intent to Singular for push tracking
SingularBridgeModule.onNewIntent(intent);
}
// Add imports at the top
import android.content.Intent
import net.singular.react_native.SingularBridgeModule
// Add to MainActivity class
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// Pass intent to Singular for push tracking
SingularBridgeModule.onNewIntent(intent)
}
또한, 위의 iOS 백그라운드/포그라운드 처리에 대해 표시된 것과 동일한 접근 방식을 사용하여 React Native 코드에서 Firebase 메시지 처리를 구현하세요.
유효성 검사 가이드
시작 세션에서 페이로드 확인
시작 세션 API 호출을 검사하여 푸시 알림 링크가 Singular에 올바르게 전달되었는지 확인합니다.
Singular SDK는 사용자가 알림을 탭할 때 시작 세션 요청의 singular_link 파라미터 아래에 푸시 알림 페이로드를 포함합니다.
세션 시작 요청 예시:
https://sdk-api-v1.singular.net/api/v1/start?
a=<SDK-Key>
&singular_link=https://singularassist2.sng.link/C4nw9/r1m0?_dl=singular%3A%2F%2Ftest&_smtype=3
&i=net.singular.sampleapp
&s=1740905574084
&sdk=Singular/React-Native-v1.0.0
대체 인증: Singular SDK 콘솔을 사용하여 푸시 알림 추적을 확인합니다. 딥링크 URL 필드를 확인하여 추적 링크가 올바르게 캡처되었는지 확인합니다.
고급 구성
ESP 도메인 구성
이메일 서비스 제공업체(ESP) 또는 기타 타사 도메인 내에 Singular 링크를 래핑하는 경우 외부 도메인을 구성합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
// Configure ESP domains for wrapped Singular links
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
espDomains: ['sl.esp.link', 'custom.domain.com']
};
NativeSingular.init(config);
import { Singular, SingularConfig } from 'singular-react-native';
// Configure ESP domains for wrapped Singular links
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withESPDomains(['sl.esp.link', 'custom.domain.com']);
Singular.init(config);
보안 참고: 기본적으로 Singular 링크 관리 페이지에 미리 정의된 sng.link도메인만 허용됩니다. 래핑 링크를 사용하는 경우 ESP 도메인을 명시적으로 구성하세요.
전체 메서드 설명서는 ESPDomains 참조를 참조하세요.
동적 딥링크 라우팅
동적 리디렉션 재정의가 있는 하나의 Singular 추적 링크를 구성하여 Singular 알림에서 여러 딥링크 대상을 구현합니다.
사용 사례 예시: 여러 작업 옵션이 포함된 속보 알림
-
최신 뉴스 읽기:
newsapp://article?id=12345 -
인기 토픽:
newsapp://trending -
스포츠:
newsapp://sports
여러 개의 추적 링크를 만드는 대신 하나의 Singular 링크를 사용하고 사용자 선택에 따라 리디렉션을 동적으로 재정의하세요. 구현에 대한 자세한 내용은 Singular 추적 링크에서 리디렉션 재정의하기를 참조하세요.
중요 고려 사항
구현 참고 사항
-
콜백 핸들러 없음:
withSingularLink과 달리 푸시 알림 기능은 페이로드 콜백을 제공하지 않습니다. 자체 딥링킹 로직을 구현하여 사용자를 앱 내의 특정 콘텐츠로 라우팅하세요. - 어트리뷰션 흐름: 사용자가 알림을 탭하면 Singular는 페이로드를 검색하여 SDK 초기화에 의해 트리거되는 시작 세션 이벤트에 포함합니다. 백엔드는 이 데이터를 처리하여 푸시 알림 터치포인트를 어트리뷰션하고 리인게이지먼트 추적을 등록합니다.
-
도메인 제한: 링크 관리 페이지의 Singular 링크 도메인(
sng.link)만 기본적으로 허용됩니다.withESPDomains()을 사용하여 래핑된 링크에 대해 명시적으로 ESP 도메인을 구성하세요. - 플랫폼 차이점: iOS는 종료된 상태에 대한 AppDelegate 구성이 필요하지만, Android는 브리지 모듈을 통해 자동으로 처리합니다.
성공: 이 단계를 수행하면 이제 앱이 Singular와 푸시 알림 상호 작용을 추적하여 캠페인 성과 인사이트를 개선하고 정확한 리인게이지먼트 어트리뷰션을 보장할 수 있습니다.