광고 구매 어트리뷰션
광고 구매을 사용자를 앱으로 유도한 특정 마케팅 캠페인에 연결하여 캠페인 비용, 인앱 구매, 광고 구매을 완벽하게 파악하여 정확한 ROI를 측정할 수 있습니다.
개요
광고 구매 어트리뷰션이란?
광고 구매 어트리뷰션은 모바일 앱 광고 구매을 사용자를 생성한 마케팅 캠페인에 연결하여 사용자 확보 비용과 광고 구매화를 포함한 평생 구매을 연결함으로써 진정한 캠페인 성과를 측정할 수 있도록 해줍니다.
- 캠페인 ROI: 연동 보고서에서 캠페인 비용, 인앱 구매, 광고 구매을 확인하여 실제 광고 투자 구매을 계산할 수 있습니다.
- 네트워크 최적화: 광고 구매 데이터를 광고 네트워크에 다시 전송하여 입찰 알고리즘과 캠페인 성과를 개선합니다.
- 데이터 소스: 애드몹, 앱러빈 맥스, 유니티 레벨플레이(아이언소스), 트레이드플러스 등 미디에이션 플랫폼의 사용자 수준 및 노출 수준 데이터를 지원합니다.
자세한 내용은 광고 구매 어트리뷰션 FAQ를 참조하세요.
중요 고려 사항:
- 통화 코드: ISO 4217 세 글자 통화 코드(USD, EUR, INR)를 사용합니다. 대부분의 미디에이션 플랫폼은 USD로 보고하므로 구현하기 전에 플랫폼의 통화를 확인하세요.
- 데이터 정확성: Singular로 전송하기 전에 구매 및 통화 데이터를 검증하세요. 잘못된 데이터는 소급하여 수정할 수 없습니다.
구현 요구 사항
광고 구매 추적을 사용하려면 미디에이션 플랫폼 SDK와 연동하고 구매 콜백을 구성해야 합니다.
- SDK 버전: 최신 Singular React Native SDK 버전으로 업데이트해야 합니다.
- 미디에이션 플랫폼: 미디에이션 플랫폼(애드몹, 앱러빈 MAX, 아이언소스, 트레이드플러스)에 React Native SDK를 연동합니다.
- 구매 콜백: 플랫폼별 유료 이벤트 핸들러를 구현하여 노출 수준 구매 데이터를 캡처하세요.
- 검증 로직: Singular로 데이터를 전송하기 전에 구매 및 통화 유효성 검사를 추가합니다.
SDK 방법
Singular.adRevenue
사용자 획득 캠페인에 대한 어트리뷰션을 위해 플랫폼, 통화 및 구매 금액과 함께 광고 구매 데이터를 Singular에 보고합니다.
메서드 서명:
static adRevenue(data: {
adPlatform: string;
currency: string;
revenue: number;
}): void
파라미터:
- 광고 플랫폼: 미디에이션 플랫폼 이름(예: "AdMob", "AppLovin", "IronSource", "TradPlus")
- 통화: ISO 4217 세 글자 통화 코드(예: "USD", "EUR")
- 구매: 지정된 통화로 표시된 구매 금액(0보다 커야 함)
전체 메서드 설명서는 광고 구매 참조를 참조하세요.
플랫폼 연동
애드몹 연동
노출 수준 구매 보고를 위해 구글 모바일 광고 SDK 유료 이벤트 콜백을 사용하여 애드몹 광고 구매 추적을 구현합니다.
전제 조건
구현 개요
광고 형식(앱 열기, 배너, 전면 광고, 네이티브, 보상형)을 로드할 때 광고에서 구매이 발생할 때 트리거되는 유료 이벤트 핸들러를 구성합니다. 이벤트 데이터에서 구매 가치와 통화를 추출하고 두 값의 유효성을 검사한 후 Singular로 전송합니다.
플랫폼 차이: 애드몹은 플랫폼별로 구매을 다르게 보고합니다. Android는 마이크로 단위(예: $0.005는 5000으로 표시)로 구매을 보고하므로 1,000,000으로 나누어야 하며, iOS는 달러 단위(0.005)로 직접 구매을 보고합니다. 플랫폼 감지를 기반으로 전환 로직을 조정합니다.
애드몹 보상형 광고 예시
애드몹으로 보상형 광고를 로드하고 구매 추적을 위해 유료 이벤트를 캡처합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import { RewardedAd, AdEventType } from 'react-native-google-mobile-ads';
import { Platform } from 'react-native';
const AD_UNIT_ID = 'ca-app-pub-xxxxxxxxxxxxx/yyyyyyyyyy';
export default function AdMobRevenueTracker() {
useEffect(() => {
loadRewardedAd();
}, []);
const loadRewardedAd = () => {
// Create RewardedAd instance
const rewardedAd = RewardedAd.createForAdRequest(AD_UNIT_ID);
// Set up event listener for ad events
rewardedAd.addAdEventListener((type, error, data) => {
if (type === AdEventType.LOADED) {
console.log('Rewarded ad loaded');
} else if (type === AdEventType.ERROR) {
console.error('Rewarded ad failed to load:', error);
} else if (type === AdEventType.PAID_EVENT) {
// Handle paid event with revenue data
handleAdRevenue(data);
}
});
// Load the ad
rewardedAd.load();
};
const handleAdRevenue = (data) => {
const { value, currencyCode } = data;
// Validate revenue and currency
if (!value || value <= 0) {
console.error('Invalid ad revenue value:', value);
return;
}
if (!currencyCode || currencyCode.trim() === '') {
console.error('Invalid currency code:', currencyCode);
return;
}
// Convert revenue based on platform
let revenue;
if (Platform.OS === 'android') {
// Android reports in micros - convert to dollars
revenue = value / 1_000_000.0;
} else {
// iOS reports in dollars directly
revenue = value;
}
const adRevenueData = {
adPlatform: 'AdMob',
currency: currencyCode,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
return null;
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import { RewardedAd, AdEventType } from 'react-native-google-mobile-ads';
import { Platform } from 'react-native';
const AD_UNIT_ID = 'ca-app-pub-xxxxxxxxxxxxx/yyyyyyyyyy';
export default function AdMobRevenueTracker() {
useEffect(() => {
loadRewardedAd();
}, []);
const loadRewardedAd = () => {
// Create RewardedAd instance
const rewardedAd = RewardedAd.createForAdRequest(AD_UNIT_ID);
// Set up event listener for ad events
rewardedAd.addAdEventListener((type, error, data) => {
if (type === AdEventType.LOADED) {
console.log('Rewarded ad loaded');
} else if (type === AdEventType.ERROR) {
console.error('Rewarded ad failed to load:', error);
} else if (type === AdEventType.PAID_EVENT) {
// Handle paid event with revenue data
handleAdRevenue(data);
}
});
// Load the ad
rewardedAd.load();
};
const handleAdRevenue = (data) => {
const { value, currencyCode } = data;
// Validate revenue and currency
if (!value || value <= 0) {
console.error('Invalid ad revenue value:', value);
return;
}
if (!currencyCode || currencyCode.trim() === '') {
console.error('Invalid currency code:', currencyCode);
return;
}
// Convert revenue based on platform
let revenue;
if (Platform.OS === 'android') {
// Android reports in micros - convert to dollars
revenue = value / 1_000_000.0;
} else {
// iOS reports in dollars directly
revenue = value;
}
const adRevenueData = {
adPlatform: 'AdMob',
currency: currencyCode,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
return null;
}
구현 참고 사항:
-
플랫폼 감지:
Platform.OS를 사용하여 전환 로직을 결정합니다(Android는 1,000,000으로 나누기, iOS는 직접 값 사용). - 구매 검증: 전송하기 전에 구매이 0보다 큰지 확인합니다.
- 통화 유효성 검사: 통화 코드가 비어 있지 않은지 확인
- 오류 로깅: Singular로 전송하지 않고 디버깅을 위해 유효하지 않은 데이터를 로깅합니다.
앱러빈 MAX 연동
모든 광고 형식에 대한 실시간 구매 보고를 위해 노출 수준 사용자 구매 API를 사용하여 AppLovin MAX 광고 구매 추적을 구현합니다.
전제 조건
- 앱러빈 MAX React Native SDK를 연동합니다. 시작 가이드참조
- 앱러빈 대시보드에서 노출 수준 사용자 구매 API를 활성화합니다.
구현 개요
구매 이벤트를 캡처하기 위해 각 광고 형식(전면 광고, 보상형, 배너, MRec, 앱 열기)에 대해 광고 구매 리스너를 구성합니다. adInfo.revenue 에서 구매을 추출하고 플랫폼별 통화(일반적으로 USD)를 사용하여 Singular로 전송합니다.
앱러빈 MAX 구매 추적
모든 앱러빈 MAX 광고 형식에 대해 글로벌 구매 리스너를 설정합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
import {
InterstitialAd,
RewardedAd,
BannerAd,
MRecAd,
AppOpenAd
} from 'react-native-applovin-max';
// Currency constant - AppLovin typically reports in USD
const CURRENCY = 'USD';
// Generic handler for ad revenue
const handleAdRevenue = (adInfo) => {
if (!adInfo) {
console.error('AdInfo is null or undefined');
return;
}
const revenue = adInfo.revenue;
// Validate revenue
if (!revenue || revenue <= 0) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: 'AppLovin',
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('AppLovin ad revenue reported:', adRevenueData);
};
// Set up listeners for each ad type
export const setupAppLovinRevenueTracking = () => {
InterstitialAd.addAdRevenuePaidListener(handleAdRevenue);
RewardedAd.addAdRevenuePaidListener(handleAdRevenue);
BannerAd.addAdRevenuePaidListener(handleAdRevenue);
MRecAd.addAdRevenuePaidListener(handleAdRevenue);
AppOpenAd.addAdRevenuePaidListener(handleAdRevenue);
console.log('AppLovin MAX revenue tracking initialized');
};
import { Singular } from 'singular-react-native';
import {
InterstitialAd,
RewardedAd,
BannerAd,
MRecAd,
AppOpenAd
} from 'react-native-applovin-max';
// Currency constant - AppLovin typically reports in USD
const CURRENCY = 'USD';
// Generic handler for ad revenue
const handleAdRevenue = (adInfo) => {
if (!adInfo) {
console.error('AdInfo is null or undefined');
return;
}
const revenue = adInfo.revenue;
// Validate revenue
if (!revenue || revenue <= 0) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: 'AppLovin',
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('AppLovin ad revenue reported:', adRevenueData);
};
// Set up listeners for each ad type
export const setupAppLovinRevenueTracking = () => {
InterstitialAd.addAdRevenuePaidListener(handleAdRevenue);
RewardedAd.addAdRevenuePaidListener(handleAdRevenue);
BannerAd.addAdRevenuePaidListener(handleAdRevenue);
MRecAd.addAdRevenuePaidListener(handleAdRevenue);
AppOpenAd.addAdRevenuePaidListener(handleAdRevenue);
console.log('AppLovin MAX revenue tracking initialized');
};
구현 참고 사항:
- 모든 광고 형식: 앱에서 사용하는 모든 광고 유형(전면 광고, 보상형, 배너, MRec, 앱 열기)에 대한 리스너를 등록합니다.
- 통화: 앱러빈은 일반적으로 구매을 USD로 보고합니다. 대시보드에서 확인하세요.
- 공유 핸들러: 모든 광고 형식에 대해 Singular 구매 핸들러 기능을 사용하여 일관된 검증을 보장합니다.
유니티 레벨플레이(아이언소스) 연동
아이언소스 광고 및 미디에이티드 네트워크의 노출 수준 데이터에 대해 ILR(노출 수준 구매) SDK API를 사용하여 아이언소스 광고 구매 추적을 구현할 수 있습니다.
전제 조건
- 아이언소스 React Native SDK를 연동합니다. 시작 가이드를참조하세요.
- IronSource 대시보드에서 ARM SDK 포스트백 플래그를 활성화합니다.
- 아이언소스 광고 구매 측정문서 검토하기
구현 개요
onImpressionDataSuccess 이벤트 이미터를 구독하여 노출 데이터를 수신합니다. impressionData.revenue 에서 구매을 추출하고 USD 통화로 Singular로 전송합니다.
아이언소스 구매 추적
IronSource 노출 데이터 콜백을 위한 이벤트 리스너를 구성합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
const { IronSourceModule } = NativeModules;
const ironSourceEventEmitter = new NativeEventEmitter(IronSourceModule);
const AD_PLATFORM = 'IronSource';
const CURRENCY = 'USD'; // IronSource typically reports in USD
export const setupIronSourceRevenueTracking = () => {
ironSourceEventEmitter.addListener('onImpressionDataSuccess', (impressionData) => {
// Validate impression data
if (!impressionData) {
console.error('No impression data available');
return;
}
const revenue = impressionData.revenue;
// Validate revenue value
if (!revenue || revenue <= 0) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('IronSource ad revenue reported:', adRevenueData);
});
console.log('IronSource revenue tracking initialized');
};
import { NativeModules, NativeEventEmitter } from 'react-native';
import { Singular } from 'singular-react-native';
const { IronSourceModule } = NativeModules;
const ironSourceEventEmitter = new NativeEventEmitter(IronSourceModule);
const AD_PLATFORM = 'IronSource';
const CURRENCY = 'USD'; // IronSource typically reports in USD
export const setupIronSourceRevenueTracking = () => {
ironSourceEventEmitter.addListener('onImpressionDataSuccess', (impressionData) => {
// Validate impression data
if (!impressionData) {
console.error('No impression data available');
return;
}
const revenue = impressionData.revenue;
// Validate revenue value
if (!revenue || revenue <= 0) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('IronSource ad revenue reported:', adRevenueData);
});
console.log('IronSource revenue tracking initialized');
};
구현 참고 사항:
- 네이티브 이벤트 이미터: IronSource는 노출 콜백을 위해 React Native의 네이티브 이벤트 시스템을 사용합니다.
- ARM 포스트백: IronSource 대시보드에서 ARM SDK 포스트백 플래그가 활성화되어 있는지 확인합니다.
- 이벤트 구독: IronSource SDK를 초기화하기 전에 리스너를 설정합니다.
TradPlus 연동
글로벌 노출 리스너를 사용하여 TradPlus 광고 구매 추적을 구현하여 광고 노출에서 eCPM 데이터를 캡처합니다.
전제 조건
- 앱에 트레이드플러스 React Native SDK를 연동합니다.
- 대시보드에서 TradPlus 광고 단위 구성
구현 개요
onImpressionSuccess 이벤트에 가입하여 광고 노출 데이터를 수신합니다. tpAdInfo.ecpm 에서 eCPM 값을 추출하고 밀리에서 달러로 변환(1000으로 나누기)한 후 Singular로 전송합니다.
eCPM 전환: TradPlus는 eCPM을 밀리 단위로 보고합니다. eCPM 값을 1000으로 나누어 달러 금액으로 변환한 후 Singular로 전송합니다.
TradPlus 구매 추적
TradPlus 노출 성공 콜백에 대한 이벤트 리스너를 구성합니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import { NativeModules, NativeEventEmitter } from 'react-native';
import NativeSingular from 'singular-react-native/jsNativeSingular';
const { TradPlusModule } = NativeModules;
const tradPlusEventEmitter = new NativeEventEmitter(TradPlusModule);
const AD_PLATFORM = 'TradPlus';
const CURRENCY = 'USD'; // TradPlus typically reports in USD
export const setupTradPlusRevenueTracking = () => {
tradPlusEventEmitter.addListener('onImpressionSuccess', (tpAdInfo) => {
// Validate ad info
if (!tpAdInfo) {
console.error('AdInfo is null');
return;
}
// eCPM is reported in milli-units - convert to dollars
if (!tpAdInfo.ecpm || typeof tpAdInfo.ecpm !== 'number') {
console.error('Invalid eCPM value:', tpAdInfo.ecpm);
return;
}
const revenue = tpAdInfo.ecpm / 1000.0;
// Validate revenue after conversion
if (revenue <= 0) {
console.error('Revenue out of expected range:', revenue);
return;
}
const adRevenueData = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('TradPlus ad revenue reported:', adRevenueData);
});
console.log('TradPlus revenue tracking initialized');
};
import { NativeModules, NativeEventEmitter } from 'react-native';
import { Singular } from 'singular-react-native';
const { TradPlusModule } = NativeModules;
const tradPlusEventEmitter = new NativeEventEmitter(TradPlusModule);
const AD_PLATFORM = 'TradPlus';
const CURRENCY = 'USD'; // TradPlus typically reports in USD
export const setupTradPlusRevenueTracking = () => {
tradPlusEventEmitter.addListener('onImpressionSuccess', (tpAdInfo) => {
// Validate ad info
if (!tpAdInfo) {
console.error('AdInfo is null');
return;
}
// eCPM is reported in milli-units - convert to dollars
if (!tpAdInfo.ecpm || typeof tpAdInfo.ecpm !== 'number') {
console.error('Invalid eCPM value:', tpAdInfo.ecpm);
return;
}
const revenue = tpAdInfo.ecpm / 1000.0;
// Validate revenue after conversion
if (revenue <= 0) {
console.error('Revenue out of expected range:', revenue);
return;
}
const adRevenueData = {
adPlatform: AD_PLATFORM,
currency: CURRENCY,
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('TradPlus ad revenue reported:', adRevenueData);
});
console.log('TradPlus revenue tracking initialized');
};
구현 참고 사항:
- eCPM 형식: TradPlus는 eCPM을 밀리 단위로 보고하며, 항상 1000으로 나눈 후 Singular로 전송합니다.
- 유형 확인: 전환 전 eCPM이 숫자인지 확인합니다.
- 전환 후 검증: 분할 후 구매이 0보다 큰지 확인합니다.
일반 연동
일반 adRevenue() 방법을 사용하여 사용자 지정 미디에이션 플랫폼 또는 직접 연동에 대한 광고 구매 추적을 구현합니다.
일반 연동 사용 시기
- 표준 연동이 적용되지 않는 커스텀 미디에이션 플랫폼
- 미디에이션 없이 직접 애드 네트워크 연동
- 앱으로 전달되는 서버 측 광고 구매 계산
- 모의 데이터로 광고 구매 추적 테스트
일반 구현 예시
적절한 검증을 통해 모든 소스의 광고 구매을 보고하는 재사용 가능한 함수를 만듭니다.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/jsNativeSingular';
/**
* Report ad revenue to Singular with validation
*
* @param {string} adPlatform - Name of the ad platform or mediation provider
* @param {string} currency - ISO 4217 three-letter currency code (e.g., 'USD', 'EUR')
* @param {number} revenue - Revenue amount in the specified currency
*/
export const reportAdRevenue = (adPlatform, currency, revenue) => {
// Validate platform
if (!adPlatform || adPlatform.trim() === '') {
console.error('Invalid ad platform:', adPlatform);
return;
}
// Validate currency code
if (!currency || currency.trim() === '' || currency.length !== 3) {
console.error('Invalid currency code:', currency);
return;
}
// Validate revenue
if (typeof revenue !== 'number' || revenue <= 0 || !isFinite(revenue)) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: adPlatform.trim(),
currency: currency.toUpperCase().trim(),
revenue: revenue
};
// Send to Singular
NativeSingular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
// Example usage
export const trackCustomAdRevenue = () => {
// Example: Custom mediation platform
reportAdRevenue('CustomPlatform', 'USD', 0.05);
// Example: Direct network integration
reportAdRevenue('FacebookAudienceNetwork', 'EUR', 0.03);
};
import { Singular } from 'singular-react-native';
/**
* Report ad revenue to Singular with validation
*
* @param {string} adPlatform - Name of the ad platform or mediation provider
* @param {string} currency - ISO 4217 three-letter currency code (e.g., 'USD', 'EUR')
* @param {number} revenue - Revenue amount in the specified currency
*/
export const reportAdRevenue = (adPlatform, currency, revenue) => {
// Validate platform
if (!adPlatform || adPlatform.trim() === '') {
console.error('Invalid ad platform:', adPlatform);
return;
}
// Validate currency code
if (!currency || currency.trim() === '' || currency.length !== 3) {
console.error('Invalid currency code:', currency);
return;
}
// Validate revenue
if (typeof revenue !== 'number' || revenue <= 0 || !isFinite(revenue)) {
console.error('Invalid revenue value:', revenue);
return;
}
const adRevenueData = {
adPlatform: adPlatform.trim(),
currency: currency.toUpperCase().trim(),
revenue: revenue
};
// Send to Singular
Singular.adRevenue(adRevenueData);
console.log('Ad Revenue reported to Singular:', adRevenueData);
};
// Example usage
export const trackCustomAdRevenue = () => {
// Example: Custom mediation platform
reportAdRevenue('CustomPlatform', 'USD', 0.05);
// Example: Direct network integration
reportAdRevenue('FacebookAudienceNetwork', 'EUR', 0.03);
};
검증 기능:
- 플랫폼 유효성 검사: 플랫폼 이름이 비어 있지 않고 잘린 것인지 확인합니다.
- 통화 유효성 검사: 세 글자 ISO 4217 코드 형식을 확인하고 대문자로 변환합니다.
- 구매 유효성 검사: 양수인지 확인, NaN 및 무한대 제외
- 유형 안전: 타입Script 인터페이스로 컴파일 시 타입 검사 보장
모범 사례
데이터 유효성 검사
강력한 유효성 검사를 구현하여 잘못된 데이터가 Singular 분석에 도달하는 것을 방지하세요.
- 양수 구매: 전송하기 전에 항상 구매이 0보다 큰지 확인합니다.
- 유효한 통화: ISO 4217 코드를 사용하고 비어 있지 않은 문자열을 확인합니다.
- 플랫폼 일관성: 앱 전체에서 일관된 플랫폼 이름을 사용하세요(예: 항상 "Admob" 또는 "ADMOB"이 아닌 "AdMob").
- 유형 검사: 데이터 유형이 예상 값과 일치하는지 확인합니다(구매의 경우 숫자, 통화/플랫폼의 경우 문자열).
- Null 검사: 널, 정의되지 않은 값, 비어있는 값을 적절하게 처리합니다.
중요: 잘못된 광고 구매 데이터는 Singular에서 소급하여 수정할 수 없습니다. Singular.adRevenue() 을 호출하기 전에 항상 데이터의 유효성을 검사하세요.
통화 처리
여러 지역의 앱과 다양한 광고 네트워크에 대한 정확한 통화 보고를 보장합니다.
- 플랫폼 통화를 확인합니다: 미디에이션 플랫폼 문서에서 기본 통화를 확인하세요(대부분 USD 사용).
- 일관된 형식: 항상 대문자 세 글자로 구성된 ISO 4217 코드를 사용하세요.
- 전환 없음: 광고 네트워크에서 제공한 통화로 구매을 보고하세요. 통화를 변환하지 마세요.
- 네트워크별 통화: 애드 네트워크마다 다른 통화로 보고할 수 있습니다 - 각각 개별적으로 확인합니다.
플랫폼별 고려 사항
구매 보고 형식 및 단위의 플랫폼별 차이를 처리합니다.
- 애드몹 Android: 마이크로 단위로 보고된 구매은 1,000,000으로 나누어 달러로 변환합니다.
- 애드몹 iOS: 전환 없이 직접 달러 사용 가치로 구매 보고
- 트레이드플러스: 밀리 단위로 보고되는 eCPM - 1,000으로 나누어 달러로 변환합니다.
- AppLovin: 달러로 보고되는 구매-사용 가치 직접 보고
- IronSource: 달러 사용 가치로 직접 보고된 구매
오류 처리 및 로깅
광고 구매 추적 디버깅 및 모니터링을 위한 포괄적인 로깅을 구현합니다.
- 유효성 검사 실패: 유효성 검사 실패 시 실제 수신된 값을 포함하여 자세한 오류 메시지를 기록합니다.
- 성공 로깅: 플랫폼, 통화 및 금액과 함께 개발 중인 성공적인 구매 보고서를 기록합니다.
- 프로덕션 모니터링: 오류 추적 서비스(센트리, 벅스내그)를 사용하여 프로덕션에서 유효성 검사 실패를 모니터링합니다.
- 구매 이상: 연동 문제를 나타낼 수 있는 비정상적으로 높거나 낮은 구매 값에 대한 알림 제공
- 플랫폼 범위: 구매을 보고하는 광고 플랫폼을 모니터링하여 모든 광고 플랫폼이 올바르게 연동되었는지 확인합니다.
테스트 전략
프로덕션 배포 전에 광고 구매 추적 구현을 확인합니다.
- 광고 테스트: 개발 중에 미디에이션 플랫폼의 테스트 광고 단위를 사용하세요.
- 이벤트 검증: 테스트 광고 노출 후 Singular 대시보드에서 광고 구매 이벤트를 확인합니다.
- 통화 확인: Singular 리포트에 통화 코드가 올바르게 표시되는지 확인합니다.
- 플랫폼 정확도: 보고서에서 플랫폼 이름이 일관되고 인식 가능한지 확인합니다.
- 구매 금액: 구매 금액: 구매 금액이 테스트 광고 단위의 예상 범위와 일치하는지 확인합니다.
- 다중 플랫폼: iOS와 안드로이드 모두에서 테스트하여 플랫폼별 전환 로직을 검증합니다.
실적 최적화
광고 구매 추적이 앱 성능에 미치는 영향을 최소화합니다.
- 비동기 처리: 구매 콜백이 비동기적으로 실행되므로 메인 스레드가 차단되지 않습니다.
- 최소한의 유효성 검사: 유효성 검사 로직을 간단하고 빠르게 유지(유형 검사, 범위 검사)
- 배치 고려 사항: 대용량 앱의 경우, 분석 백엔드에서 지원하는 경우 일괄 처리를 고려하세요.
- 오류 처리: 트라이 캐치 블록을 사용하여 구매 추적 오류로 인한 앱 충돌을 방지하세요.
검증 및 문제 해결
구현 확인
광고 구매 데이터가 Singular로 올바르게 전송되는지 확인합니다.
- 테스트 광고를 활성화합니다: 미디에이션 플랫폼에서 테스트 모드를 구성합니다.
- 노출 트리거: 테스트 광고를 표시하고 유료 이벤트를 트리거합니다.
- 로그 확인: 구매 추적 로그가 콘솔에 올바른 값으로 표시되는지 확인합니다.
- 대시보드 확인: Singular 대시보드에서 광고 구매 이벤트를 확인합니다(15~30분 정도 소요될 수 있음).
- 이벤트 세부 정보: Singular 이벤트 세부 정보에서 통화, 플랫폼, 구매 금액을 확인합니다.
일반적인 문제
- 구매 이벤트가 없습니다: 광고를 로딩하기 전에 유료 이벤트 핸들러가 등록되어 있고 미디에이션 SDK가 올바르게 초기화되었는지 확인합니다.
- 제로 구매: 플랫폼별 전환 로직 확인 (안드로이드 애드몹의 경우 마이크로에서 달러로, 트레이드플러스의 경우 밀리에서 달러로)
- 잘못된 통화: 통화 코드가 미디에이션 플랫폼이 보고하는 것과 일치하는지 확인 - 플랫폼 문서 확인
- 플랫폼 이름 불일치: Singular가 인식하는 플랫폼과 일치하는 일관된 플랫폼 이름을 사용하세요.
- 누락된 이벤트: 광고 구매 이벤트가 발생하기 전에 Singular SDK가 초기화되었는지 확인하세요.
- 중복 이벤트: 유료 이벤트 핸들러가 광고를 로드할 때마다 등록되지 않고 한 번만 등록되었는지 확인합니다.
추가 리소스: 자세한 내용은 광고 구매 어트리뷰션 FAQ및 React Native SDK 메서드 참조를 참조하세요.