디바이스 데이터 검색 가이드
정확한 S2S API 어트리뷰션 및 캠페인 측정에 필요한 플랫폼별 디바이스 식별자 및 파라미터 검색을 위한 종합 가이드입니다.
필수 기기 식별자: Singular는 정확한 어트리뷰션을 위해 모든 API 요청에 특정 디바이스 식별자를 요구합니다.
모바일 플랫폼:
- 안드로이드(구글 플레이): 구글 광고 ID(GAID/AIFA) 및 앱 세트 ID(ASID)
- 안드로이드(아마존): Fire 기기용 아마존 광고 ID(AMID)
- Android(중국 OEM): Google Play 서비스가 없는 기기용 오픈 광고 ID(OAID)
- Android(대체): 다른 식별자가 없는 경우에만 안드로이드 ID(ANDI) 사용
- iOS: 사용 가능한 경우 벤더용 식별자(IDFV) 및 광고주용 식별자(IDFA)를 사용합니다.
디바이스 매개변수: 모바일 플랫폼에 필요한 로캘, 디바이스 제조사, 디바이스 모델 및 빌드 버전
아래 코드 예시는 각 플랫폼 및 식별자 유형에 대한 검색 방법을 보여줍니다.
샘플 애플리케이션
참조 구현
디바이스 데이터 검색 패턴을 보여주는 iOS 및 Android용 전체 작업 예제입니다.
iOS 디바이스 식별자
iOS 디바이스에는 정확한 어트리뷰션을 위해 IDFV(항상) 및 IDFA(사용자가 추적 권한을 부여한 경우)와 ATT 승인 상태가 필요합니다.
필수 iOS 식별자
식별자 요구 사항:
- IDFV: 추적 권한에 관계없이 모든 S2S 요청에 필요
- IDFA: 사용자가 앱 추적 투명성 동의를 부여한 경우 제공해야 합니다.
- ATT 상태: 모든 요청에 필요한 권한 상태 코드(0-3)
구현 가이드
광고주 식별자(IDFA)
광고주 식별자(IDFA)를 사용하면 광고주가 정확한 타겟팅 및 최적화를 위해 사용자 행동(광고 클릭, 앱 설치)을 추적하고 특정 캠페인에 어트리뷰션할 수 있습니다.
iOS 14.5부터는 앱이 IDFA에 액세스하기 전에 사용자가 ATT(앱 추적 투명성) 프레임워크를 통해 옵트인해야 합니다. 사용자의 동의가 없으면 IDFA는 모든 0을 반환하여 추적 기능을 제한합니다.
벤더용 식별자(IDFV)
공급업체용 식별자(IDFV)는 Apple이 공급업체/개발자별로 기기에 할당하는 고유 식별자입니다. 기기의 동일한 공급업체의 모든 앱에서 일관성을 유지하여 개인 식별 없이 앱 간 행동 추적을 가능하게 합니다.
구현 단계:
- IDFA 액세스를 시도하기 전에 ATT 프롬프트가 표시되고 처리되는지 확인합니다.
- IDFA를 캡처(권한이 부여된 경우)하고 API 요청을 위해 서버로 전달합니다.
- IDFV를 캡처하여 API 요청을 위해 서버로 전달(항상 필요)
- 모든 요청에 ATT 권한 부여 상태 포함
코드 예제
ATT 권한 요청 및 IDFA/IDFV 가져오기
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <UIKit/UIKit.h>
- (void)retrieveIdentifiers {
// Request ATT authorization (iOS 14.5+)
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (status) {
case ATTrackingManagerAuthorizationStatusAuthorized: {
// ATT authorized, retrieve IDFA
NSUUID *idfa = [[ASIdentifierManager sharedManager] advertisingIdentifier];
NSLog(@"IDFA: %@", [idfa UUIDString]);
NSLog(@"ATT Status: %ld", (long)status); // Status = 3
break;
}
case ATTrackingManagerAuthorizationStatusDenied:
NSLog(@"ATT Status: Denied (%ld)", (long)status); // Status = 2
break;
case ATTrackingManagerAuthorizationStatusRestricted:
NSLog(@"ATT Status: Restricted (%ld)", (long)status); // Status = 1
break;
case ATTrackingManagerAuthorizationStatusNotDetermined:
NSLog(@"ATT Status: Not Determined (%ld)", (long)status); // Status = 0
break;
default:
NSLog(@"Unknown ATT status.");
break;
}
// Retrieve IDFV (always available)
NSUUID *idfv = [[UIDevice currentDevice] identifierForVendor];
if (idfv != nil) {
NSLog(@"IDFV: %@", [idfv UUIDString]);
} else {
NSLog(@"Unable to retrieve IDFV.");
}
});
}];
}
// Call the method to retrieve identifiers
[self retrieveIdentifiers];
import AdSupport
import AppTrackingTransparency
import UIKit
func retrieveIdentifiers() {
// Request ATT authorization (iOS 14.5+)
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// ATT authorized, retrieve IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA: \(idfa)")
print("ATT Status: \(status.rawValue)") // Status = 3
case .denied:
print("ATT Status: Denied (\(status.rawValue))") // Status = 2
case .restricted:
print("ATT Status: Restricted (\(status.rawValue))") // Status = 1
case .notDetermined:
print("ATT Status: Not Determined (\(status.rawValue))") // Status = 0
@unknown default:
print("Unknown ATT status.")
}
// Retrieve IDFV (always available)
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("IDFV: \(idfv)")
} else {
print("Unable to retrieve IDFV.")
}
}
}
}
// Call the function to retrieve identifiers
retrieveIdentifiers()
식별자 가용성:
- IDFA: iOS 14.5 이상에서 ATT 인증이 필요합니다. 동의가 없으면 모든 0을 반환합니다.
- IDFV: 항상 사용 가능-모든 Singular API 요청에 포함
- ATT 상태 값: 0=미정, 1=제한됨, 2=거부됨, 3=승인됨
Android 기기 식별자(Google Play)
구글 플레이 서비스를 사용하는 안드로이드 기기는 모든 요청에 앱 세트 ID(ASID)가 필요하며, 사용 가능한 경우 구글 광고 ID(GAID/AIFA)가 필요합니다.
필수 Google Play 식별자
식별자 요구 사항:
- ASID: Google Play 기기에 대한 모든 S2S 요청에 필수입니다.
- AIFA/GAID: 사용 가능한 경우 제공해야 함(옵트아웃하지 않음)
구현 가이드
구글 광고 식별자(GAID)
AIFA 또는 안드로이드 광고 ID(AAID)라고도 하는 구글 광고 식별자(GAID)는 안드로이드 기기에 할당된 사용자 재설정 가능한 고유 식별자입니다. 광고주와 개발자가 개인정보 보호를 유지하면서 캠페인 타겟팅 및 최적화를 위해 앱 전반에서 사용자 행동을 추적하고 어트리뷰션할 수 있도록 지원합니다.
종속성
build.gradle 에 필수 종속성을 추가하세요:
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
권한
Android 12/API 레벨 31 이상을 타겟팅하는 경우, AndroidManifest.xml 에 권한을 추가합니다:
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
사용
AdIdUtils.getGoogleAdId(getApplicationContext());
구현
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
public class AdIdUtils {
public static void getGoogleAdId(Context context) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
String adId = adInfo.getId();
boolean isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
Log.d("GoogleAdID", "Advertising ID: " + adId);
Log.d("GoogleAdID", "Limit Ad Tracking: " + isLimitAdTrackingEnabled);
} catch (Exception e) {
Log.e("GoogleAdID", "Error retrieving GAID", e);
}
}
});
}
}
종속성
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
권한
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
사용법
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
val googleAdId = AdIdUtils.getGoogleAdId(applicationContext)
Log.d("MainActivity", "Retrieved Google Ad ID: $googleAdId")
}
}
}
구현
import android.content.Context
import android.util.Log
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object AdIdUtils {
suspend fun getGoogleAdId(context: Context): String? {
return withContext(Dispatchers.IO) {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
val adId = adInfo.id
val isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled
Log.d("GoogleAdID", "Advertising ID: $adId")
Log.d("GoogleAdID", "Limit Ad Tracking: $isLimitAdTrackingEnabled")
adId
} catch (e: Exception) {
Log.e("GoogleAdID", "Error retrieving GAID", e)
null
}
}
}
}
앱 세트 ID(ASID)
Android 앱 세트 ID는 동일한 개발자에 대해 개인정보 보호를 고려한 교차 앱 추적을 제공합니다. 분석 및 사기 방지에는 유용하지만 개인화된 광고에는 사용할 수 없습니다.
종속성
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
사용법
AppSetIdUtils.getAppSetId(getApplicationContext());
구현
import android.content.Context;
import android.util.Log;
import com.google.android.gms.appset.AppSet;
import com.google.android.gms.appset.AppSetIdClient;
import com.google.android.gms.appset.AppSetIdInfo;
import com.google.android.gms.tasks.Task;
public class AppSetIdUtils {
public static void getAppSetId(Context context) {
AppSetIdClient client = AppSet.getClient(context);
Task task = client.getAppSetIdInfo();
task.addOnSuccessListener(info - {
String appSetId = info.getId();
int scope = info.getScope();
Log.d("AppSetID", "App Set ID: " + appSetId);
Log.d("AppSetID", "Scope: " + (scope == AppSetIdInfo.SCOPE_DEVELOPER ? "Developer" : "App"));
}).addOnFailureListener(e - {
Log.e("AppSetID", "Failed to retrieve App Set ID", e);
});
}
}
종속성
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
사용법
AppSetIdUtils.getAppSetId(applicationContext)
구현
import android.content.Context
import android.util.Log
import com.google.android.gms.appset.AppSet
import com.google.android.gms.appset.AppSetIdClient
import com.google.android.gms.appset.AppSetIdInfo
object AppSetIdUtils {
fun getAppSetId(context: Context) {
val client: AppSetIdClient = AppSet.getClient(context)
val task = client.appSetIdInfo
task.addOnSuccessListener { info -
val appSetId: String = info.id
val scope: Int = info.scope
Log.d("AppSetID", "App Set ID: $appSetId")
Log.d("AppSetID", "Scope: ${if (scope == AppSetIdInfo.SCOPE_DEVELOPER) "Developer" else "App"}")
}.addOnFailureListener { exception -
Log.e("AppSetID", "Failed to retrieve App Set ID", exception)
}
}
}
Android 기기 식별자(비구글플레이)
Google Play 서비스가 없는 Android 기기에는 기기 제조업체 및 배포 방식에 따라 대체 식별자가 필요합니다.
아마존 기기 식별자
AMID: 구글 플레이 서비스가 없는 아마존 파이어 기기의 경우 아마존 광고 ID를 제공해야 합니다.
아마존 ID(AMID)
Amazon 광고 식별자를사용하면 구글 플레이 서비스가 없는 아마존 파이어 기기에서 사용자가 재설정 가능한 광고 추적이 가능하여 사용자 개인정보는 유지하면서 어트리뷰션을 활성화할 수 있습니다.
요구 사항:
- Fire OS 5.1 이상을 실행하는 Amazon Fire 기기에서 작동합니다.
- 사용자의 광고 추적 제한 기본 설정을 존중합니다.
- Fire OS가 아닌 기기에서는 사용할 수 없습니다.
사용 방법
AdvertisingIdHelper.getAmazonAdvertisingId(getContentResolver());
구현
import android.content.ContentResolver;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
public class AdvertisingIdHelper {
public static void getAmazonAdvertisingId(ContentResolver contentResolver) {
String advertisingID = "";
boolean limitAdTracking = false;
try {
limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0;
advertisingID = Settings.Secure.getString(contentResolver, "advertising_id");
Log.d("AdvertisingID", "Amazon Advertising ID: " + advertisingID);
Log.d("LimitAdTracking", "Limit Ad Tracking: " + limitAdTracking);
} catch (SettingNotFoundException e) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e);
}
}
}
사용 방법
AdvertisingIdHelper.getAmazonAdvertisingId(contentResolver)
구현
import android.content.ContentResolver
import android.provider.Settings
import android.util.Log
object AdvertisingIdHelper {
fun getAmazonAdvertisingId(contentResolver: ContentResolver) {
try {
val limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0
val advertisingID = Settings.Secure.getString(contentResolver, "advertising_id")
Log.d("AdvertisingID", "Amazon Advertising ID: $advertisingID")
Log.d("LimitAdTracking", "Limit Ad Tracking: $limitAdTracking")
} catch (e: Settings.SettingNotFoundException) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e)
}
}
}
중국 OEM 식별자
OAID: 구글 플레이 서비스가 없는 중국 제조 기기의 경우 오픈 광고 식별자를 제공해야 합니다.
오픈 광고 ID(OAID)
OAID(오픈 광고 식별자)는 중국에서 제조된 Android 기기에서 광고를 위한 고유한 익명 식별자입니다. 모바일 보안 연합(MSA)에서 구글 플레이 서비스를 사용할 수 없는 기기를 위한 GAID의 대안으로 도입했습니다.
지원되는 기기: 화웨이, 샤오미, OPPO, 비보 및 기타 중국 제조 안드로이드 기기
MSA SDK또는 Huawei 모바일 서비스(HMS)를 통해 액세스합니다.
종속성
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
구현
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.bun.msa.sdk.DeviceId;
import com.bun.msa.sdk.DeviceIdSupplier;
import com.bun.msa.sdk.IIdentifierListener;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "OAIDExample";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getOAID();
}
private void getOAID() {
try {
DeviceId deviceId = new DeviceId(this);
deviceId.getDeviceIds(new IIdentifierListener() {
@Override
public void onSupport(boolean isSupport, DeviceIdSupplier supplier) {
if (isSupport && supplier != null) {
String oaid = supplier.getOAID();
Log.d(TAG, "OAID: " + oaid);
} else {
Log.e(TAG, "OAID not supported on this device");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Error retrieving OAID", e);
}
}
}
종속성
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
구현
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.bun.msa.sdk.DeviceId
import com.bun.msa.sdk.DeviceIdSupplier
import com.bun.msa.sdk.IIdentifierListener
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "OAIDExample"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getOAID()
}
private fun getOAID() {
try {
val deviceId = DeviceId(this)
deviceId.getDeviceIds(object : IIdentifierListener {
override fun onSupport(isSupport: Boolean, supplier: DeviceIdSupplier?) {
if (isSupport && supplier != null) {
val oaid = supplier.oAID
Log.d(TAG, "OAID: $oaid")
} else {
Log.e(TAG, "OAID not supported on this device")
}
}
})
} catch (e: Exception) {
Log.e(TAG, "Error retrieving OAID", e)
}
}
}
Android ID 폴백
ANDI 제한 사항: Android ID는 다른 식별자가 없고 Google Play 스토어를 통해 배포되지 않은 앱에만 제공될 수 있습니다. Google Play 앱에는 금지되어 있습니다.
안드로이드 ID(ANDI)
안드로이드 ID는 기기를 처음 설정할 때 생성되는 64비트 고유 식별자입니다. 안드로이드 8.0(오레오)부터 앱별, 사용자별로 범위가 지정되어 앱마다 동일한 서명 키를 공유하지 않는 한 서로 다른 안드로이드 ID를 받습니다.
지속성: 기기를 초기화하거나 OTA 업데이트 후 앱을 삭제/재설치하지 않는 한 일정하게 유지됩니다.
import android.provider.Settings;
import android.content.Context;
String androidId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID
);
import android.provider.Settings
val androidId: String = Settings.Secure.getString(
contentResolver,
Settings.Secure.ANDROID_ID
)
웹 및 크로스 플랫폼 식별자
웹 애플리케이션 및 크로스 플랫폼 구현에는 정확한 어트리뷰션 추적을 위해 SDID(Singular 디바이스 ID)가 필요합니다.
필수 웹 식별자
SDID: 웹, PC, 콘솔, CTV 플랫폼에 대한 모든 S2S 요청에 필요한 Singular 디바이스 ID입니다.
Singular 웹 SDK 디바이스 ID
Singular 디바이스 ID(SDID)는 웹 애플리케이션 및 비모바일 플랫폼에 대해 일관된 교차 세션 추적을 제공합니다.
전제 조건: SDID를 검색하기 전에 Singular Web SDK를 구현하고 초기화해야 합니다.
사용 방법
// Retrieve SDID after Singular SDK initialization
const sdid = window.singularSdk.getSingularDeviceId();
console.log("Singular Device ID:", sdid);
구현 참고: Singular SDK가 성공적으로 초기화된 후에만 getSingularDeviceId()을 호출하세요. 초기화 전에 검색을 시도하면 null이 반환됩니다.
모바일 디바이스 파라미터
필수 디바이스 파라미터는 모바일 플랫폼에서 어트리뷰션 및 분석에 필수적인 컨텍스트를 제공합니다.
필수 파라미터
모바일 플랫폼: 로캘, 기기 제조사, 기기 모델, 빌드는 iOS와 안드로이드에 대한 모든 S2S 요청에 필요합니다.
파라미터 검색
완전한 디바이스 프로파일링을 위해 로캘, 제조사, 모델, 빌드 정보를 수집합니다.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sys/sysctl.h>
// Retrieve Locale
NSString *retrieveLocale() {
NSString *locale = [[NSLocale currentLocale] localeIdentifier];
NSLog(@"Locale: %@", locale);
return locale;
}
// Retrieve Manufacturer (always Apple for iOS)
NSString *retrieveManufacturer() {
return @"Apple";
}
// Retrieve Device Model
NSString *deviceModel() {
size_t bufferSize = 64;
char model[bufferSize];
int status = sysctlbyname("hw.machine", model, &bufferSize, NULL, 0);
if (status == 0) {
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
NSLog(@"Device Model: %@", deviceModel);
return deviceModel;
} else {
NSLog(@"Unable to retrieve device model.");
return nil;
}
}
// Retrieve Build Version
NSString *buildVersion() {
size_t bufferSize = 64;
char build[bufferSize];
int status = sysctlbyname("kern.osversion", build, &bufferSize, NULL, 0);
if (status == 0) {
NSString *buildVersion = [NSString stringWithCString:build encoding:NSUTF8StringEncoding];
NSLog(@"Build Version: %@", buildVersion);
return buildVersion;
} else {
NSLog(@"Unable to retrieve build version.");
return nil;
}
}
import Foundation
import UIKit
// Retrieve Locale
func retrieveLocale() - String {
let locale = Locale.current.identifier
print("Locale: \(locale)")
return locale
}
// Retrieve Manufacturer (always Apple for iOS)
func retrieveManufacturer() - String {
return "Apple"
}
// Retrieve Device Model
func deviceModel() - String? {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
print("Device Model: \(identifier)")
return identifier
}
// Retrieve Build Version
func buildVersion() - String? {
var size: Int = 0
sysctlbyname("kern.osversion", nil, &size, nil, 0)
var build = [CChar](repeating: 0, count: size)
sysctlbyname("kern.osversion", &build, &size, nil, 0)
let buildVersion = String(cString: build)
print("Build Version: \(buildVersion)")
return buildVersion
}
import android.os.Build;
import java.util.Locale;
// Locale (lc parameter)
String locale = Locale.getDefault().toString();
// Device Make (ma parameter)
String deviceMake = Build.MANUFACTURER;
// Device Model (mo parameter)
String deviceModel = Build.MODEL;
// Build (bd parameter)
String build = "Build/" + Build.ID;
import android.os.Build
import java.util.Locale
// Locale (lc parameter)
val locale: String = Locale.getDefault().toString()
// Device Make (ma parameter)
val deviceMake: String = Build.MANUFACTURER
// Device Model (mo parameter)
val deviceModel: String = Build.MODEL
// Build (bd parameter)
val build: String = "Build/" + Build.ID
파라미터 매핑:
- 로캘(lc): 언어 및 지역 코드(예: en_US, zh_CN)
- 제조사(ma): 디바이스 제조업체(Apple, 삼성, 샤오미)
- 모델(mo): 특정 디바이스 모델(iPhone14,2, SM-G991B)
- 빌드(bd): "빌드/" 접두사가 붙은 OS 빌드 버전