광고 매출 어트리뷰션
미디에이션 플랫폼에서 발생하는 광고 매출을 추적하고, 사용자를 획득한 마케팅 캠페인에 귀속시켜 캠페인 비용, 인앱 구매, 광고 구매화 전반에 걸친 완전한 ROI 가시성을 제공합니다.
개요
광고 매출 어트리뷰션이란
광고 매출 어트리뷰션은 모바일 앱 광고 매출을 앱 설치를 유도한 사용자 획득 캠페인과 연결하여, 광고 구매화를 포함한 진정한 캠페인 구매성을 측정할 수 있도록 합니다.
주요 이점:
- 연동 ROI 뷰: 캠페인 비용, 인앱 매출, 광고 매출을 하나의 대시보드에서 확인
- 캠페인 최적화: 광고 매출 데이터를 광고 네트워크로 다시 전송하여 입찰 및 타겟팅 개선
- LTV 측정: 광고 구매화를 포함한 사용자의 완전한 라이프타임 밸류 계산
데이터 소스: 광고 매출 데이터는 일반적으로 사용자 수준 또는 임프레션 수준에서 미디에이션 플랫폼(예: AdMob, AppLovin MAX, IronSource)으로부터 제공됩니다. Singular는 이 데이터를 수신하기 위한 여러 연동 방식을 지원합니다.
더 알아보기: 설정, 리포팅, 문제 해결에 관한 자세한 내용은 광고 매출 어트리뷰션 FAQ 를 참조하세요.
구현 요구사항
핵심 가이드라인
필수 SingularAdData 파라미터:
생성자 인수
adPlatform,
withCurrency,
withRevenue
는 모두 필수입니다. 값이 누락되었거나 비어 있거나 유효하지 않은 경우
(-hasRequiredParams 기준),
SDK는
+adRevenue:
호출을 예외 발생 없이 조용히 폐기합니다. SDK가 시작되지 않은 상태에서도
호출이 조용히 무시되므로, 호출 전에 항상
+start:
가 실행되었는지 확인하세요.
올바른 생성자 시그니처 사용:
-initWithAdPlatform:withCurrency:withRevenue:
가 지원되는 생성자입니다. 레거시
-initWithAdPlatfrom:
(오타 주의)는
__attribute__((deprecated))
로 표시되어 있어 컴파일러 중요를 발생시킵니다. 새 코드에서는 사용하지 마세요.
데이터 정확성이 매우 중요합니다:
- 통화 코드: 세 글자 ISO 4217 통화 코드를 사용하세요 (예: USD, EUR, INR). 많은 미디에이션 플랫폼이 USD로 리포팅하므로, 구현 전에 플랫폼의 통화를 검증하세요
-
전송 전 검증:
Singular.adRevenue()를 호출하기 전에 항상 매출 및 통화 데이터를 검증하세요. 잘못된 데이터는 제출 후 수정할 수 없습니다 - 플랫폼 간 차이: iOS는 마이크로 단위를 사용하는 Android와 달리, 표준 통화 단위(예: $0.005)로 매출을 직접 수신합니다. 올바른 형식에 대해서는 항상 플랫폼 문서를 확인하세요
설정 단계
광고 매출 어트리뷰션을 구현하려면 다음 단계를 따르세요:
- SDK 업데이트: 최신 Singular SDK 버전을 사용하고 있는지 확인
- 연동 방식 선택: 설정에 맞는 아래 미디에이션 플랫폼 연동을 선택
- 콜백 구현: 매출 데이터를 캡처하기 위한 플랫폼별 paid 이벤트 리스너 추가
- 데이터 검증: 매출 리포팅을 테스트하고 데이터가 Singular 대시보드에 표시되는지 확인
플랫폼 연동
AdMob 연동
Google AdMob에서 발생하는 광고 매출을 임프레션 단위 매출 리포팅을 위한 paid 이벤트 리스너를 사용하여 추적합니다.
요구사항:
플랫폼 매출 리포팅: AdMob은 플랫폼에 따라 매출을 다르게 리포팅합니다. iOS는 표준 통화 단위(예: $0.005 = 0.005)로 매출을 반환합니다. 변환 없이 값을 Singular에 그대로 전송하세요.
구현
광고를 로드할 때 paid 이벤트 핸들러를 설정하여 매출 데이터를 캡처하고 Singular로 전송합니다.
import Singular
import GoogleMobileAds
private let adUnitID = "AD_UNIT_ID"
var rewardedAd: GADRewardedAd?
func loadRewardedAd() {
let request = GADRequest()
GADRewardedAd.load(withAdUnitID: adUnitID, request: request) { [weak self] ad, error in
guard let self = self else { return }
if let error = error {
print("Rewarded ad failed to load: \(error.localizedDescription)")
return
}
self.rewardedAd = ad
// Set paid event handler for revenue tracking
self.rewardedAd?.paidEventHandler = { adValue in
// Extract revenue and currency from AdValue
let revenue = adValue.value.doubleValue
let currency = adValue.currencyCode
// Validate revenue and currency before sending
guard revenue > 0, let currency = currency, !currency.isEmpty else {
print("Invalid ad revenue data: revenue = \(revenue), currency = \(String(describing: currency))")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AdMob",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
}
}
#import <Singular/Singular.h>
#import <GoogleMobileAds/GoogleMobileAds.h>
static NSString *const adUnitID = @"AD_UNIT_ID";
@interface AdManager ()
@property(nonatomic, strong) GADRewardedAd *rewardedAd;
@end
@implementation AdManager
- (void)loadRewardedAd {
GADRequest *request = [[GADRequest alloc] init];
[GADRewardedAd loadWithAdUnitID:adUnitID
request:request
completionHandler:^(GADRewardedAd *ad, NSError *error) {
if (error) {
NSLog(@"Rewarded ad failed to load: %@", error.localizedDescription);
return;
}
self.rewardedAd = ad;
// Set paid event handler for revenue tracking
self.rewardedAd.paidEventHandler = ^(GADAdValue *adValue) {
// Extract revenue and currency from AdValue
double revenue = adValue.value.doubleValue;
NSString *currency = adValue.currencyCode;
// Validate revenue and currency before sending
if (revenue > 0 && currency.length > 0) {
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"AdMob"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
} else {
NSLog(@"Invalid ad revenue data: revenue = %f, currency = %@",
revenue, currency);
}
};
}];
}
@end
AppLovin MAX 연동
AppLovin Impression-Level User Revenue API를 사용하여 임프레션 수준 광고 매출을 공유합니다.
요구사항:
- AppLovin MAX SDK를 구현하세요 ( Impression-Level Revenue API 가이드 참조)
-
AppLovin Communicator를 통해
max_revenue_events토픽을 구독하세요
구현
AppLovin Communicator 콜백을 통해 매출 메시지를 처리합니다.
import Singular
import AppLovinSDK
func didReceive(_ message: ALCMessage) {
// Check for revenue events topic
if message.topic == "max_revenue_events" {
// Extract and validate revenue value
guard let revenueValue = message.data["revenue"] as? Double,
revenueValue > 0 else {
print("Failed to parse valid revenue value or revenue is not greater than 0")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AppLovin",
currency: "USD", // AppLovin typically reports in USD
revenue: revenueValue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenueValue) USD")
}
}
#import <Singular/Singular.h>
#import <AppLovinSDK/AppLovinSDK.h>
- (void)didReceive:(ALCMessage *)message {
// Check for revenue events topic
if ([@"max_revenue_events" isEqualToString:message.topic]) {
NSDictionary *data = message.data;
// Extract and validate revenue value
NSNumber *revenueNumber = data[@"revenue"];
double revenueValue = [revenueNumber doubleValue];
if (revenueValue > 0) {
// Create ad revenue data object
SingularAdData *adData = [[SingularAdData alloc]
initWithAdPlatform:@"AppLovin"
currency:@"USD"
revenue:revenueValue];
// Send to Singular
[Singular adRevenue:adData];
NSLog(@"Ad revenue sent: %f USD", revenueValue);
} else {
NSLog(@"Failed to parse valid revenue value or revenue is not greater than 0");
}
}
}
Unity LevelPlay (IronSource) 연동
IronSource SDK를 사용하여 ironSource 및 미디에이션 네트워크에서 발생하는 임프레션 수준 매출을 추적합니다.
요구사항:
- ironSource SDK를 구현하세요 ( 시작 가이드 참조)
- IronSource 대시보드에서 ARM SDK Postbacks 플래그를 활성화하세요
- 매출 콜백을 수신하도록 impression data 리스너를 설정하세요
더 알아보기: 자세한 설정 내용은 IronSource 광고 매출 문서 를 참조하세요.
구현
impression data success 콜백을 구현하여 매출을 캡처하고 전송합니다.
import Singular
import IronSource
func impressionDataDidSucceed(_ impressionData: ISImpressionData?) {
// Validate impression data
guard let impressionData = impressionData else {
print("No impression data available")
return
}
// Extract and validate revenue
let revenue = impressionData.revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "IronSource",
currency: "USD", // IronSource typically reports in USD
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) USD")
}
#import <Singular/Singular.h>
#import <IronSource/IronSource.h>
- (void)impressionDataDidSucceed:(ISImpressionData *)impressionData {
// Validate impression data
if (!impressionData) {
NSLog(@"No impression data available");
return;
}
// Extract and validate revenue
double revenue = impressionData.revenue;
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"IronSource"
currency:@"USD"
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f USD", revenue);
}
TradPlus 연동
impression delegate를 사용하여 TradPlus 미디에이션에서 발생하는 광고 매출을 캡처합니다.
요구사항:
-
TradPlus.sharedInstance().impressionDelegate를 통해 impression delegate를 설정하세요 -
매출 데이터를 수신하기 위해
tradPlusAdImpression콜백을 처리하세요 - eCPM을 밀리 단위에서 표준 통화 단위로 변환하세요 (1000으로 나눔)
구현
모든 광고 임프레션과 매출을 추적하기 위해 impression delegate를 등록합니다.
import Singular
import TradPlusSDK
// Set up the delegate
TradPlus.sharedInstance().impressionDelegate = self
// Delegate method for handling ad impressions
func tradPlusAdImpression(_ adInfo: [String: Any]) {
let currency = "USD"
// Extract and validate eCPM value
guard let ecpmValue = adInfo["ecpm"] as? NSNumber else {
print("No eCPM data available in adInfo")
return
}
// Convert eCPM from milli-units to dollars
let revenue = ecpmValue.doubleValue / 1000.0
// Validate revenue
guard revenue > 0 else {
print("Ad Revenue value out of expected range: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "TradPlus",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
#import <Singular/Singular.h>
#import <TradPlusSDK/TradPlusSDK.h>
// Set up the delegate
TradPlus.sharedInstance.impressionDelegate = self;
// Delegate method for handling ad impressions
- (void)tradPlusAdImpression:(NSDictionary<NSString *, id> *)adInfo {
NSString *currency = @"USD";
// Extract and validate eCPM value
NSNumber *ecpmValue = adInfo[@"ecpm"];
if (!ecpmValue) {
NSLog(@"No eCPM data available in adInfo");
return;
}
// Convert eCPM from milli-units to dollars
double revenue = [ecpmValue doubleValue] / 1000.0;
// Validate revenue
if (revenue <= 0) {
NSLog(@"Ad Revenue value out of expected range: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"TradPlus"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
}
일반 연동 (기타 플랫폼)
일반
SingularAdData
인터페이스를 사용하여 모든 미디에이션 플랫폼을 연동합니다.
요구사항:
- 미디에이션 플랫폼의 임프레션 수준 매출 데이터에 접근 가능해야 함
- 표준 통화 단위의 매출 금액
- ISO 4217 통화 코드 (예: USD, EUR, INR)
데이터 정확성: Singular로 전송하기 전에 매출 및 통화 데이터를 검증하세요. 잘못된 데이터는 제출 후 수정할 수 없습니다.
구현
플랫폼 이름, 통화, 매출이 포함된
SingularAdData
객체를 생성한 후
Singular.adRevenue()
를 호출합니다.
import Singular
func reportAdRevenue(adPlatform: String, currency: String, revenue: Double) {
// Validate revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Validate currency
guard !currency.isEmpty else {
print("Invalid currency: \(currency)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: adPlatform,
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Revenue sent: \(revenue) \(currency) from \(adPlatform)")
}
// Example usage
reportAdRevenue(adPlatform: "MyMediationPlatform", currency: "USD", revenue: 0.05)
#import <Singular/Singular.h>
- (void)reportAdRevenueWithPlatform:(NSString *)adPlatform
currency:(NSString *)currency
revenue:(double)revenue {
// Validate revenue
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Validate currency
if (!currency || currency.length == 0) {
NSLog(@"Invalid currency: %@", currency);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:adPlatform
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Revenue sent: %f %@ from %@", revenue, currency, adPlatform);
}
// Example usage
[self reportAdRevenueWithPlatform:@"MyMediationPlatform"
currency:@"USD"
revenue:0.05];
추가 속성으로 광고 데이터 강화
SingularAdData 는 임프레션 수준 메타데이터를 위한 setter 메서드를
제공합니다. 미디에이션 플랫폼에서 제공되는 경우, 이 메서드를 사용하여 배치,
광고 유닛, 미디에이션 그룹화, 정확성 세부 정보로 이벤트를 강화하세요.
Android SDK와 달리,
SingularAdData
setter는
void
를 반환하며 체이닝이 불가능합니다. 각 setter를 개별 라인에서 호출하세요.
| 메서드 | 설명 |
|---|---|
setNetworkName:
|
네트워크 이름을 재정의합니다 (기본값은 생성자에 전달된 광고 플랫폼). 미디에이션에서 기본 네트워크가 미디에이션 플랫폼과 다른 경우에 사용합니다. |
setAdType:
|
광고 형식 (예:
@"Rewarded",
@"Interstitial",
@"Banner").
|
setGroupType:
|
미디에이션 플랫폼에서 정의한 광고 그룹 분류. |
setImpressionId:
|
임프레션 고유 식별자로, 중복 제거 및 대조에 사용됩니다. |
setAdPlacementName:
|
사람이 읽을 수 있는 배치 이름 (예:
@"level_complete").
|
setAdUnitId:
|
플랫폼 고유의 광고 유닛 식별자 (예: AdMob의 경우
@"ca-app-pub-..."
).
|
setAdUnitName:
|
사람이 읽을 수 있는 광고 유닛 이름. |
setAdGroupId:
|
미디에이션 플랫폼의 광고 그룹 식별자. |
setAdGroupName:
|
사람이 읽을 수 있는 광고 그룹 이름. |
setAdGroupPriority:
|
미디에이션 워터폴에서 광고 그룹에 할당된 우선순위. |
setPrecision:
|
매출 정확성 지표 (플랫폼에서 지원하는 경우, 예:
@"publisher_provided",
@"estimated",
@"exact").
|
setPlacementId:
|
플랫폼 고유의 배치 식별자. |
setLimitDataSharing:
|
이 개별 광고 이벤트에 대해 전역 데이터 공유 제한을 재정의합니다. 예를 들어 사용자가 맞춤형 광고를 거부한 경우 사용합니다. |
Swift 예시:
let data = SingularAdData(adPlatform: "AdMob",
withCurrency: "USD",
withRevenue: NSNumber(value: 0.05))
data.setAdUnitId("ca-app-pub-123456789/1234567890")
data.setAdType("Rewarded")
data.setAdPlacementName("level_complete")
data.setImpressionId("imp_abc123")
data.setPrecision("publisher_provided")
Singular.adRevenue(data)
Objective-C 예시:
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"AdMob"
withCurrency:@"USD"
withRevenue:@(0.05)];
[data setAdUnitId:@"ca-app-pub-123456789/1234567890"];
[data setAdType:@"Rewarded"];
[data setAdPlacementName:@"level_complete"];
[data setImpressionId:@"imp_abc123"];
[data setPrecision:@"publisher_provided"];
[Singular adRevenue:data];
테스트 및 검증
매출 리포팅 확인
데이터가 Singular로 올바르게 전달되는지 확인하려면 광고 매출 구현을 테스트하세요.
- 로그 확인: 올바른 값과 통화로 매출 콜백 로그가 나타나는지 검증
- 테스트 광고: 매출 이벤트를 발생시키도록 테스트 광고를 로드하고 표시
- 대시보드 검증: 24시간 이내에 Singular 대시보드에 매출이 표시되는지 확인
- 데이터 정확성: 매출 금액이 미디에이션 플랫폼 리포트와 일치하는지 검증
문제 해결: Singular에 매출이 표시되지 않는 경우 다음을 확인하세요:
- Singular 계정에서 광고 매출 어트리뷰션이 활성화되어 있음
- 매출 값이 0보다 큼
- 통화 코드가 유효한 ISO 4217 코드임
- 플랫폼 이름이 Singular의 예상 형식과 일치함