레거시 — SKAdNetwork 3. 이 가이드는 레거시 SKAdNetwork 3.0 구현을 다루며 기존 연동에 한해서만 유지됩니다. 새로운 구현 및 최신 구현에는 모두 SKAdNetwork 4 구현 가이드 를 사용하세요.
SKAdNetwork 3.0 S2S 구현 가이드 (레거시)
서버 간(server-to-server) 연동을 사용해 Apple의 SKAdNetwork 3.0 프레임워크를 구현하여, 유저 식별자를 침해하지 않고 프라이버시를 준수하는 iOS 어트리뷰션으로 앱 설치 캠페인 성과를 측정하세요.
개요
SKAdNetwork란?
SKAdNetwork는 유저 수준 식별자 없이도 iOS에서 앱 설치 광고 캠페인의 전환율을 측정할 수 있게 해주는 Apple의 프라이버시 보호 어트리뷰션 프레임워크입니다.
이 프레임워크는 App Store 서버를 통해 어트리뷰션을 처리하며, 광고 네트워크에 전송하기 전에 어트리뷰션 정보를 유저 식별자 및 시간 데이터와 분리합니다.
Apple 문서: SKAdNetwork Framework Reference
SKAdNetwork 작동 방식
어트리뷰션 프로세스는 전적으로 Apple의 인프라를 통해 이루어지며, 캠페인 성과 측정을 가능하게 하면서도 유저 프라이버시를 보장합니다.
어트리뷰션 플로우:
- 광고 클릭: 유저가 네트워크, 퍼블리셔, 캠페인 ID를 담은 SKAdNetwork 서명이 포함된 광고를 클릭합니다
- App Store 열기: 기기가 어트리뷰션 데이터를 저장하고 설치를 위해 App Store를 엽니다
- 앱 실행: 유저가 앱을 설치하고 처음으로 실행합니다
- 등록: 앱이 성공적인 설치를 알리기 위해 SKAdNetwork 프레임워크에 등록합니다
- Conversion Value: 앱이 설치 후 활동을 나타내는 conversion value(0-63)를 선택적으로 업데이트합니다
- 타이머 윈도우: 기기가 최초 실행 또는 마지막 conversion value 업데이트로부터 24시간 이상 대기합니다
- 포스트백: App Store가 캠페인 ID, conversion value, 암호화 서명과 함께 어트리뷰션 포스트백을 광고 네트워크로 전송합니다
설계에 의한 프라이버시(Privacy by Design):
- 포스트백에는 기기 또는 유저 식별자가 포함되지 않습니다
- 최소 24시간의 지연으로 시간 기반 상관관계 분석을 방지합니다
- 앱은 유저가 어떤 광고를 클릭했는지 절대 알 수 없습니다
- 네트워크는 어떤 특정 유저가 설치했는지 절대 알 수 없습니다
기능 및 제한 사항
SKAdNetwork가 제공하는 것:
- 라스트 클릭 어트리뷰션: 유저 동의나 ATT 옵트인 없이도 작동합니다
- 캠페인 세분화: 소스, 캠페인, 퍼블리셔 단위의 세분화
- Conversion Value: 설치 후 측정을 위한 64개의 개별 값(0-63)
- 부정 방지: 암호화 서명으로 어트리뷰션의 진위를 검증합니다
현재 제한 사항:
- 유저 수준 데이터 없음: 개별 유저 여정을 추적할 수 없습니다
- 뷰스루 없음: 클릭 기반 어트리뷰션만 지원합니다
- 제한된 Conversion Value: 설치당 단일 6비트 값(0-63)
- 제한된 세분화: 최대 100개의 캠페인 ID, 광고 그룹 또는 크리에이티브 단위 세분화 없음
- 장기 코호트 없음: 전환 측정 기간이 제한적입니다
- 부정 노출: conversion value 자체는 서명되지 않으므로 포스트백이 중복될 수 있습니다
Singular 리소스
Singular는 SKAdNetwork 구현 및 최적화를 위한 종합적인 리소스를 제공합니다.
- SKAN: Practical Standard for SKAdNetwork Implementation
- SKAdNetwork 101: What It Means for You
- SKAdNetwork Code: GitHub Repository for Networks, Publishers, Advertisers
- How to Test SKAdNetwork: Step-by-Step Instructions
- Singular Announces First-to-Market IDFA Replacement Support
- Advanced Measurement Using SKAdNetwork: Unlocking ROAS
- Secure SKAdNetwork 2.0: Seamless Trust Establishment Setup
Singular SKAdNetwork 솔루션
Singular의 SKAdNetwork 솔루션은 클라이언트 측 구현부터 포스트백 처리 및 캠페인 최적화에 이르기까지 엔드투엔드 어트리뷰션 관리를 제공합니다.
솔루션 구성 요소
플랫폼 기능
어트리뷰션 및 분석 워크플로 전반에 걸친 종합적인 SKAdNetwork 지원.
| 구성 요소 | 기능 |
|---|---|
| 클라이언트 측 코드 | SKAdNetwork 프레임워크 등록 및 conversion value 관리를 위한 네이티브 iOS 코드 샘플 |
| 포스트백 처리 | 모든 광고 네트워크의 포스트백을 검증 및 집계하고 연동 리포팅으로 제공 |
| 부정 방지 | 암호화 서명 검증, 트랜잭션 ID 중복 제거, 안전한 파라미터 검증 |
| 전환 관리 | 설치 후 활동을 conversion value로 인코딩하기 위한 동적 대시보드 구성 |
| 리포팅 | 세분화된 분석을 위한 캠페인 ID 변환 및 마케팅 파라미터를 통한 보강 |
| 파트너 포스트백 | 파트너 최적화를 위해 디코딩된 conversion value를 이벤트 및 매출로 전송 |
S2S 연동 아키텍처
구현 구성 요소
서버 간 SKAdNetwork 연동은 두 가지 주요 구현 영역으로 구성됩니다.
1. 클라이언트 측 구현 (필수):
- 앱 실행 시 SKAdNetwork 프레임워크 등록
- 설치 후 활동에 기반한 지능형 conversion value 관리
- SKAdNetwork 어트리뷰션을 사용한 캠페인 최적화에 필수적
2. S2S 연동 업데이트 (선택 사항):
- Singular S2S 이벤트와 세션을 SKAdNetwork 메타데이터로 보강
- 구현 검증 및 트러블슈팅 지원
- 디버깅을 위한 conversion value 타임스탬프 제공
클라이언트 측 구현
최적의 캠페인 측정을 위해 Singular의 네이티브 iOS 코드 샘플을 사용하여 SKAdNetwork 프레임워크 등록과 conversion value 관리를 구현하세요.
구현 책임 범위
핵심 기능
클라이언트 측 코드는 SKAdNetwork 측정을 위한 두 가지 핵심 기능을 처리합니다.
- SKAdNetwork 등록: 어트리뷰션을 활성화하기 위해 실행 직후 앱을 프레임워크에 등록합니다
-
Conversion Value 관리:
- Singular 엔드포인트와 동기식으로 통신하여 다음 conversion value를 수신합니다
- 세션, 이벤트, 매출을 Singular에 보고합니다
- 구성된 설치 후 활동을 나타내는 인코딩된 conversion value를 수신합니다
- SKAdNetwork 메타데이터(최초 호출 타임스탬프, 마지막 호출 타임스탬프, 현재 값)를 수집합니다
SKAdNetwork 인터페이스
메서드 정의
인터페이스에는 프레임워크 등록, 세션 트래킹, 이벤트 트래킹, 매출 측정을 위한 메서드가 포함됩니다.
// SKANSnippet.h
#import <Foundation/Foundation.h>
@interface SKANSnippet : NSObject
// Register for SKAdNetwork attribution.
// Call this method as soon as possible once app is launched
+ (void)registerAppForAdNetworkAttribution;
// Track retention and cohorts by calling after each session.
// Reports session details and updates conversion value if needed.
// Conversion value updates only if new value greater than previous.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler;
// Track conversion events by calling after each event.
// Reports event details and updates conversion value if needed.
// Optional callback runs once conversion value updated.
+ (void)updateConversionValueAsync:(NSString*)eventName
withCompletionHandler:(void(^)(int, NSError*))handler;
// Track revenue by calling before each revenue event.
// Updates total revenue for next conversion value calculation.
// Note:
// 1. Call before 'updateConversionValueAsync' to ensure revenue included
// 2. Avoid calling on event retries to prevent double-counting
+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency;
// Gets current conversion value (nil if none set)
+ (NSNumber *)getConversionValue;
@end
등록 메서드
registerAppForAdNetworkAttribution
는 앱을 SKAdNetwork 프레임워크에 등록하여 24시간 어트리뷰션 타이머를
시작합니다.
타이머 동작:
- 기기는 타이머 만료 후 0-24시간 이내에 설치 알림을 전송합니다
- 더 높은 값으로 conversion value를 업데이트하면 타이머가 새로운 24시간 간격으로 재설정됩니다
- Apple 참고 자료: updateConversionValue Documentation
Conversion Value 메서드
이 메서드들은 설치 후 활동과 구성된 전환 모델을 기반으로 conversion value를 계산하고 업데이트합니다.
지원되는 활동:
- 세션: 리텐션 측정에 필수적입니다
- 전환 이벤트: 설치 후 이벤트 측정에 필수적입니다
- 매출 이벤트: 매출 측정에 필수적입니다
인터페이스 구현
전체 구현 코드
전체 구현은 등록, 전환 관리, Singular API 통신을 처리합니다.
// SKANSnippet.m
#import "SKANSnippet.h"
#import <StoreKit/SKAdNetwork.h>
#define SESSION_EVENT_NAME @"__SESSION__"
#define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v1/conversion_value"
// Keys for UserDefaults storage
#define CONVERSION_VALUE_KEY @"skan_conversion_value"
#define FIRST_SKAN_CALL_TIMESTAMP @"skan_first_call_to_skadnetwork_timestamp"
#define LAST_SKAN_CALL_TIMESTAMP @"skan_last_call_to_skadnetwork_timestamp"
#define TOTAL_REVENUE_BY_CURRENCY_KEY @"skan_total_revenue_by_currency"
#define SECONDS_PER_DAY 86400
static NSLock *lockObject;
@implementation SKANSnippet
+ (void)registerAppForAdNetworkAttribution {
if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
return;
}
if (@available(iOS 11.3, *)) {
[SKAdNetwork registerAppForAdNetworkAttribution];
[SKANSnippet setFirstSkanCallTimestamp];
[SKANSnippet setLastSkanCallTimestamp];
}
}
+ (void)updateConversionValueAsync:(void(^)(int, NSError*))handler {
[SKANSnippet updateConversionValueAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}
+ (void)updateConversionValueAsync:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
if (@available(iOS 14, *)) {
if ([SKANSnippet isSkanUpdateWindowOver]) {
return;
}
[SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
}
}
+ (void)updateRevenue:(double)amount andCurrency:(NSString*)currency {
if (@available(iOS 14, *)) {
// Update total revenues
if (amount != 0 && currency) {
NSMutableDictionary* revenues = [[SKANSnippet getTotalRevenue] mutableCopy];
NSNumber* currentRevenue = @(0);
if ([revenues objectForKey:currency]) {
currentRevenue = [revenues objectForKey:currency];
}
currentRevenue = @([currentRevenue floatValue] + amount);
[revenues setObject:currentRevenue forKey:currency];
[SKANSnippet setTotalRevenue:revenues];
}
}
}
+ (NSNumber *)getConversionValue {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
return nil;
}
return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
}
+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
if (!lockObject) {
lockObject = [NSLock new];
}
// Making the lock async so it will not freeze the calling thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[lockObject lock];
NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];
NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
components.queryItems = @[
[NSURLQueryItem queryItemWithName:@"a" value:@"YOUR_SDK_KEY"],
[NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
[NSURLQueryItem queryItemWithName:@"app_v" value:@"YOUR_APP_VERSION"],
[NSURLQueryItem queryItemWithName:@"n" value:eventName],
[NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
[NSURLQueryItem queryItemWithName:@"idfv" value:@"YOUR_IDFV"],
[NSURLQueryItem queryItemWithName:@"idfa" value:@"YOUR_IDFA"],
[NSURLQueryItem queryItemWithName:@"conversion_value" value:[[SKANSnippet getConversionValue] stringValue]],
[NSURLQueryItem queryItemWithName:@"total_revenue_by_currency" value:[SKANSnippet dictionaryToJsonString:[SKANSnippet getTotalRevenue]]],
[NSURLQueryItem queryItemWithName:@"first_call_to_skadnetwork_timestamp" value:[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]]],
[NSURLQueryItem queryItemWithName:@"last_call_to_skadnetwork_timestamp" value:[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]],
];
[[[NSURLSession sharedSession] dataTaskWithURL:components.URL
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
[lockObject unlock];
if (handler) {
handler(-1, error);
}
return;
}
NSDictionary* parsedResponse = [SKANSnippet jsonDataToDictionary:data];
if (!parsedResponse) {
[lockObject unlock];
if (handler) {
handler(-1, [NSError errorWithDomain:bundleIdentifier
code:0
userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
}
return;
}
NSNumber *conversionValue = [parsedResponse objectForKey:@"conversion_value"];
if (!conversionValue) {
[lockObject unlock];
NSString *status = [parsedResponse objectForKey:@"status"];
if (!status || ![status isEqualToString:@"ok"]) {
if (handler) {
NSString *reason = [parsedResponse objectForKey:@"reason"];
if (!reason) {
reason = @"Got error from server";
}
handler(-1, [NSError errorWithDomain:bundleIdentifier
code:0
userInfo:@{NSLocalizedDescriptionKey:reason}]);
}
}
return;
}
NSNumber* currentValue = [SKANSnippet getConversionValue];
if ([conversionValue intValue] <= [currentValue intValue]) {
[lockObject unlock];
return;
}
[SKANSnippet setConversionValue:[conversionValue intValue]];
[SKANSnippet setLastSkanCallTimestamp];
if (![SKANSnippet getFirstSkanCallTimestamp]) {
[SKANSnippet setFirstSkanCallTimestamp];
}
[lockObject unlock];
if (handler) {
handler([conversionValue intValue], error);
}
}] resume];
});
}
+ (BOOL)isSkanUpdateWindowOver {
NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getLastSkanCallTimestamp];
return SECONDS_PER_DAY <= timeDiff;
}
@end
유틸리티 메서드
메타데이터 관리
유틸리티 메서드는 conversion value 계산에 필수적인 SKAdNetwork 메타데이터를 UserDefaults에 저장하고 관리합니다.
중요 구현 사항: 이 유틸리티들은 정확한 conversion value 계산에 필요한 메타데이터를 유지합니다. 필요한 경우가 아니면 수정하지 마세요.
+ (NSInteger)getFirstSkanCallTimestamp {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults integerForKey:FIRST_SKAN_CALL_TIMESTAMP];
}
+ (NSInteger)getLastSkanCallTimestamp {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults integerForKey:LAST_SKAN_CALL_TIMESTAMP];
}
+ (NSDictionary*)getTotalRevenue {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
if (![userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY]) {
return [NSDictionary new];
}
return [userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY];
}
+ (NSInteger)getCurrentUnixTimestamp {
return [[NSDate date]timeIntervalSince1970];
}
+ (void)setConversionValue:(int)value {
if (@available(iOS 14.0, *)) {
if (value <= [[SKANSnippet getConversionValue] intValue]) {
return;
}
[SKAdNetwork updateConversionValue:value];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:value forKey:CONVERSION_VALUE_KEY];
[userDefaults synchronize];
}
}
+ (void)setFirstSkanCallTimestamp {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp]
forKey:FIRST_SKAN_CALL_TIMESTAMP];
[userDefaults synchronize];
}
+ (void)setLastSkanCallTimestamp {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp]
forKey:LAST_SKAN_CALL_TIMESTAMP];
[userDefaults synchronize];
}
+ (void)setTotalRevenue:(NSDictionary *)values {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:values forKey:TOTAL_REVENUE_BY_CURRENCY_KEY];
[userDefaults synchronize];
}
+ (NSString*)dictionaryToJSONString:(NSDictionary*)dictionary {
if (!dictionary || [dictionary count] == 0){
return @"{}";
}
NSError *error;
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:dictionary
options:0
error:&error];
if (error || !JSONData) {
return @"{}";
}
return [[NSString alloc] initWithData:JSONData
encoding:NSUTF8StringEncoding];
}
+ (NSDictionary*)JSONDataToDictionary:(NSData*)JSONData {
if (!JSONData) {
return nil;
}
NSError * error;
NSDictionary * parsedData = [NSJSONSerialization
JSONObjectWithData:JSONData
options:kNilOptions error:&error];
if (error || !parsedData) {
return nil;
}
return parsedData;
}
@end
S2S 연동 업데이트
선택 사항으로, 구현 검증 및 트러블슈팅을 위해 서버 간 연동을 SKAdNetwork 메타데이터로 보강할 수 있습니다.
필수 업데이트
Conversion Value 응답
전환 모델이 활성화되면 S2S 엔드포인트는 클라이언트 측 업데이트를 위한 다음 값을
담은
conversion_value
정수 필드를 반환합니다.
응답 처리:
S2S 엔드포인트 응답에서
conversion_value
를 파싱한 후 클라이언트 측 코드를 통해 SKAdNetwork 프레임워크에
적용하세요.
선택적 개선 사항
메타데이터 파라미터
S2S 세션 및 이벤트 요청을 SKAdNetwork 메타데이터로 보강하여 연동을 검증하고 구현 문제를 트러블슈팅하세요.
| 파라미터 | 설명 |
|---|---|
skan_conversion_value
|
요청 시점의 최신 conversion value |
skan_first_call_timestamp
|
SKAdNetwork API에 대한 최초 호출의 Unix 타임스탬프 |
skan_last_call_timestamp
|
SKAdNetwork API에 대한 최신 호출의 Unix 타임스탬프 |
메타데이터 추출 코드
유틸리티 메서드를 사용하여 S2S 전송을 위한 SKAdNetwork 메타데이터 값을 추출하세요.
NSDictionary *skanMetadata = @{
@"skan_conversion_value":
[[SKANSnippet getConversionValue] stringValue],
@"skan_first_call_timestamp":
[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]],
@"skan_last_call_timestamp":
[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]
};
// Forward skanMetadata to your server for S2S API enrichment
파라미터를 서버 측으로 전달한 후 Singular S2S API 요청에 추가하세요. 전체 파라미터 문서: S2S API Reference
연동 플로우
클라이언트 측 전환 관리부터 포스트백 처리에 이르기까지, S2S 고객을 위한 전체 SKAdNetwork 플로우입니다.
플로우 단계
엔드투엔드 프로세스
- Conversion Value 요청: 앱 코드가 세션, 이벤트, 매출을 기반으로 최신 conversion value를 얻기 위해 Singular 엔드포인트와 동기식으로 통신합니다
- 프레임워크 업데이트: 앱이 수신한 conversion value로 SKAdNetwork 프레임워크를 업데이트합니다
- 메타데이터 보강: 앱이 검증을 위해 S2S 이벤트와 세션을 SKAdNetwork 메타데이터로 보강합니다
- 타이머 만료: 마지막 업데이트로부터 24시간 이상 경과한 후, SKAdNetwork가 광고 네트워크로 포스트백을 전송합니다
- 포스트백 전달: 네트워크가 포스트백을 Singular로 전달합니다(secure 설정 또는 일반 설정)
-
포스트백 처리:
Singular는 다음과 같이 포스트백을
처리합니다:
- 암호화 서명 검증
- 구성된 모델을 사용하여 conversion value 디코딩
- 파트너 연동을 기반으로 마케팅 파라미터로 보강
- 포스트백을 통해 디코딩된 데이터를 BI 및 파트너에 전송
데이터 분리: SKAdNetwork 데이터(설치 및 디코딩된 이벤트)는 테스트 및 검증 중 기존 데이터셋과의 혼재를 방지하기 위해 별도의 리포트, API, ETL 테이블, 포스트백을 통해 접근할 수 있습니다.
앱 라이프사이클 구현
완전한 어트리뷰션 커버리지를 위해 적절한 앱 라이프사이클 지점에 SKAdNetwork 메서드를 연동하세요.
구현 예시
메서드 배치
// On app launch (in applicationDidFinishLaunching)
[SKANSnippet registerAppForAdNetworkAttribution];
// After each session handled
[SKANSnippet updateConversionValueAsync:^(int value, NSError *error) {
if (error) {
NSLog(@"Conversion value update failed: %@", error);
} else {
NSLog(@"Conversion value updated to: %d", value);
}
}];
// After handling non-revenue events
[SKANSnippet updateConversionValueAsync:@"event_name"
withCompletionHandler:^(int value, NSError *error) {
if (error) {
NSLog(@"Event conversion value update failed: %@", error);
} else {
NSLog(@"Event conversion value updated to: %d", value);
}
}];
// After handling revenue events
[SKANSnippet updateRevenue:15.53 andCurrency:@"USD"];
[SKANSnippet updateConversionValueAsync:@"revenue_event_name"
withCompletionHandler:^(int value, NSError *error) {
if (error) {
NSLog(@"Revenue conversion value update failed: %@", error);
} else {
NSLog(@"Revenue conversion value updated to: %d", value);
}
}];
구현 테스트
프로덕션 배포 전에 Singular의 테스트 엔드포인트를 사용하여 SKAdNetwork 구현을 검증하세요.
테스트 환경
테스트 엔드포인트 구성
앱 플로우를 시뮬레이션하기 위해 프로덕션 전환 관리 엔드포인트를 테스트 엔드포인트로 교체하세요.
테스트 엔드포인트 URL:
https://skadnetwork-testing.singular.net/api/v1/conversion_value
- 테스트 엔드포인트에는 API 키가 필요하지 않습니다
- conversion value 0-2를 순차적으로 반환합니다
- 세 번째 호출 이후에는 빈 문자열을 반환합니다
테스트 절차
검증 단계
-
엔드포인트 업데이트:
SINGULAR_API_URL상수를 테스트 엔드포인트 URL로 교체합니다 - Conversion Value 초기화: 기본 conversion value가 0으로 초기화되었는지 확인합니다
- 이벤트 생성: 앱 내에서 3개의 서로 다른 이벤트를 트리거합니다
-
업데이트 확인:
각 이벤트 후에
updateConversionValueAsync가 호출되는지 확인합니다 - 값 로깅: 반환된 conversion value를 로깅하여 올바른 진행(0 → 1 → 2)을 확인합니다
- 완료 확인: 세 번째 호출 이후 빈 응답과 최종 값 유지를 확인합니다
예상 동작:
- 첫 번째 이벤트: conversion value 0 수신
- 두 번째 이벤트: conversion value 1 수신
- 세 번째 이벤트: conversion value 2 수신
- 네 번째 이상의 이벤트: 빈 응답, 값 2 유지
코드 변경 이력
시간 경과에 따라 적용된 코드 샘플 업데이트 및 중요 수정 사항을 추적합니다.
버전 이력
| 날짜 | 변경 사항 |
|---|---|
| 2020년 10월 1일 |
|
| 2020년 9월 23일 |
|
| 2020년 9월 15일 |
|