Server-to-Server - 모바일 앱 S2S 가이드

개요

이 가이드는 공유 S2S 메커니즘 위에 더해지는 모바일 전용 어트리뷰션 신호, 즉 Apple Search Ads, Google Play 및 Meta 설치 리퍼러, 앱 삭제 추적, iOS 설치 영수증을 다룹니다. 세션과 이벤트를 server-to-server 방식으로 리포트하는 iOS 및 Android 앱 연동에 적용됩니다.

이 기능들은 공유 S2S 요청 흐름이 이미 구축되어 있다고 가정합니다. 이 기능들이 기반으로 하는 엔드포인트, 인증, 공통 파라미터에 대해서는 먼저 fundamentals 허브와 두 개의 엔드포인트 레퍼런스를 참고하세요.

시작하기 전에: 이 기능들은 공유 S2S 요청 흐름을 기반으로 합니다. 엔드포인트와 공통 파라미터는 S2S Fundamentals 허브 를 참고하고, SESSION 엔드포인트 레퍼런스 EVENT 엔드포인트 레퍼런스 도 함께 확인하세요.


Apple Search Ads 어트리뷰션

iOS Search Ads 연동

Apple Search Ads는 Self-Attributing Network(SAN)로, Apple 프레임워크를 통한 플랫폼별 어트리뷰션 구현이 필요합니다.

프레임워크 지원:

이중 구현: iAd가 지원 중단될 때까지 iAd와 AdServices 프레임워크를 모두 구현하세요. Singular는 어트리뷰션과 리포팅에서 iAd 신호보다 AdServices를 우선합니다.

전체 가이드: Apple Search Ads 연동 문서


iAd 프레임워크 구현

iOS 14.2 이하에서 iAd 프레임워크를 통해 Apple Search Ads 어트리뷰션 데이터를 가져와 전송합니다.

1단계: 어트리뷰션 데이터 가져오기

Objective-C
#import <iAd/iAd.h>

Class ADClientClass = NSClassFromString(@"ADClient");

if (ADClientClass) {
    id sharedClient = [ADClientClass performSelector:@selector(sharedClient)];

    if ([sharedClient respondsToSelector:@selector(requestAttributionDetailsWithBlock:)]) {
        [sharedClient requestAttributionDetailsWithBlock:^(NSDictionary *attributionDetails, NSError *error) {
            if (attributionDetails && attributionDetails.count > 0) {
                // REPORT attributionDetails FROM YOUR APP TO YOUR SERVER
            }
        }];
    }
}

지연 시간 처리: Search Ads를 클릭한 사용자는 앱을 즉시 다운로드하고 열 수 있습니다. 다음 방법으로 어트리뷰션 타이밍 문제를 방지하세요:

  1. 어트리뷰션 데이터를 가져오기 전에 몇 초의 지연을 설정합니다
  2. 응답이 FALSE이거나 오류 코드(0, 2, 3)인 경우 재시도 로직을 구현합니다. 2초 후에 재시도하세요

2단계: Singular로 전송

예약된 이름 __iAd_Attribution__ 으로 EVENT 엔드포인트 를 통해 이벤트를 리포트하며, 어트리뷰션 JSON을 e 파라미터로 전달합니다.

타이밍 요구사항:

  • iOS 13+: 설치/재설치 후 첫 세션 직후에 __iAd_Attribution__ 이벤트를 전송하세요. 그렇지 않으면 Apple Search Ads 데이터가 어트리뷰션에 반영되지 않습니다
  • iOS 14+: 어트리뷰션 응답은 특정 조건에서만 사용할 수 있으며, ATT 상태가 ATTrackingManager.AuthorizationStatus.denied 인 경우 사용할 수 없습니다

AdServices 프레임워크 구현

iOS 14.3 이상에서 AdServices 프레임워크를 통해 어트리뷰션 토큰을 가져와 전송합니다.

1단계: 어트리뷰션 토큰 가져오기

Objective-C
#import <AdServices/AdServices.h>

NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
    NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
    if (!error && attributionToken) {
        // Handle attributionToken
    }
}

토큰 특성:

  • 기기에서 생성됩니다
  • 5분간 캐시됩니다—만료 후 attributionToken() 을 호출하면 새 토큰이 생성됩니다
  • 24시간 동안 유효합니다

2단계: Singular로 전송

토큰을 URL 인코딩하여 모든 설치 및 재설치 후 첫 세션에서 SESSION 엔드포인트 attribution_token 파라미터로 추가합니다.


Google Play 설치 리퍼러

Android 설치 어트리뷰션

Google Play 설치 리퍼러는 가장 정확한 Android 설치 어트리뷰션을 제공하며, Play Store에 도달하기 전 사용자 출처에 대한 정보를 포함합니다.

필수 대상:

자세한 정보: Google Install Referrer 문서

구현 단계:

  1. 첫 앱 실행 시 Play Install Referrer API 를 사용하여 설치 리퍼러를 가져옵니다
  2. 필수 속성이 포함된 install_ref JSON 파라미터를 포함하여 SESSION 엔드포인트 를 통해 세션을 리포트합니다

install_ref 속성:

파라미터 세부 정보
referrer

Play Install Referrer API의 리퍼러 값(JSON 객체—문자열로 인코딩)

referrer_source

"service"로 지정

clickTimestampSeconds

API의 클릭 타임스탬프(예: "1550420123")

installBeginTimestampSeconds

API의 설치 시작 시간

current_device_time

현재 기기 시간(밀리초 단위, 예: "1550420454906")


Meta 설치 리퍼러

Facebook 어트리뷰션 강화

2025년 6월 18일부로: Meta의 Advanced Mobile Measurement (AMM) 은 Meta 설치 리퍼러 구현의 필요성을 없앱니다. AMM이 활성화된 경우 권장하지 않습니다.

Meta Referrer는 Android 전용 측정 솔루션으로, Google Play 설치 리퍼러와 Meta 설치 리퍼러 기술을 결합하여 앱 설치에 대한 세분화된 유저 레벨 어트리뷰션 데이터를 제공합니다.

자세히 알아보기: Meta Referrer FAQ

구현 단계:

  1. 첫 앱 실행 시 Meta 문서 에 따라 Meta 설치 리퍼러를 가져옵니다
  2. 필수 속성이 포함된 meta_ref JSON 파라미터를 포함하여 SESSION 엔드포인트 를 통해 세션을 리포트합니다

meta_ref 속성:

파라미터 세부 정보
is_ct

Meta 설치 리퍼러의 is_ct(0 또는 1)

install_referrer

Meta 설치 리퍼러의 install_referrer

actual_timestamp

Meta 설치 리퍼러의 actual_timestamp(예: 1693978124)


앱 삭제 추적

사일런트 푸시 알림 설정

모든 세션 알림과 함께 푸시 토큰을 전송하여 기기의 사일런트 푸시 알림을 사용해 앱 삭제를 추적합니다.

설정 요구사항:

  1. 플랫폼별 앱 삭제 추적 설정을 따르세요:
  2. 플랫폼별 토큰을 SESSION 요청에 추가하세요:

iOS 설치 영수증

영수증 검증

iOS 세션을 리포트할 때 iOS 설치 영수증을 install_receipt 파라미터로 전달합니다.

Swift
import Foundation
import StoreKit

class ReceiptManager {
    static func getInstallReceipt() -> String? {
        if #available(iOS 18.0, *) {
            let semaphore = DispatchSemaphore(value: 0)
            var result: String?

            Task {
                do {
                    let transaction = try await AppTransaction.shared
                    result = transaction.jwsRepresentation
                    semaphore.signal()
                } catch {
                    debugPrint("Failed to get app transaction: \(error.localizedDescription)")
                    semaphore.signal()
                }
            }

            semaphore.wait()
            return result

        } else {
            guard let receiptURL = Bundle.main.appStoreReceiptURL else {
                debugPrint("Receipt URL not found")
                return nil
            }

            do {
                let receiptData = try Data(contentsOf: receiptURL, options: .uncached)
                return receiptData.base64EncodedString(options: [])
            } catch {
                debugPrint("Failed to read receipt: \(error.localizedDescription)")
                return nil
            }
        }
    }
}