안내가 다를 수 있으므로 Android의 개인 정보 보호 샌드박스 문서를 읽으면서 개발자 프리뷰 또는 베타 버튼을 사용하여 작업 중인 프로그램 버전을 선택하세요.
Android의 Protected Audience API(이전의 FLEDGE)에는 Custom Audience API와 Ad Selection API가 포함되어 있습니다. 광고 기술 플랫폼과 광고주는 앱 간 식별자 공유를 제한하고 사용자의 앱 상호작용 정보를 서드 파티와 공유하는 것을 제한하는 이전 앱 참여를 토대로 맞춤 광고를 처리하는 데 다음 API를 사용할 수 있습니다.
Custom Audience API는 공통의 의도가 있는 사용자 그룹을 나타내는 '맞춤 잠재고객' 추상화를 중심으로 합니다. 광고주는 맞춤 잠재고객이 있는 사용자를 등록하고 관련 광고를 이에 연결할 수 있습니다. 이 정보는 로컬에 저장되며 광고주 입찰, 광고 필터링 및 광고 렌더링을 알리는 데 사용될 수 있습니다.
Ad Selection API는 여러 개발자가 맞춤 잠재고객을 대상으로 로컬에서 입찰을 실행할 수 있는 프레임워크를 제공합니다. 이를 위해 시스템은 맞춤 잠재고객과 연결된 관련 광고를 고려하고, 광고 기술 플랫폼을 통해 기기에 반환되는 광고에 추가 처리를 실행합니다.
광고 기술 플랫폼은 이러한 API를 통합하여 사용자 개인 정보 보호를 보존하는 리마케팅을 구현할 수 있습니다. 앱 설치 광고를 비롯한 추가 사용 사례 지원은 향후 출시 버전에 계획되어 있습니다. 설계 제안서에서 Android의 Protected Audience API에 관해 자세히 알아보세요.
이 가이드에서는 Android의 Protected Audience API를 사용하여 다음을 실행하는 방법을 설명합니다.
시작하기 전에
시작하기 전에 다음을 완료하세요.
- Android의 개인 정보 보호 샌드박스의 개발 환경을 설정합니다.
- 지원되는 기기에 시스템 이미지를 설치하거나 Android의 개인 정보 보호 샌드박스 지원이 포함된 에뮬레이터를 설정합니다.
터미널에서 다음 adb 명령어를 사용하여 Protected Audience API에 대한 액세스를 사용 설정합니다(기본적으로 사용 중지되어 있음).
adb shell device_config put adservices ppapi_app_allow_list \"*\"
앱 매니페스트에
ACCESS_ADSERVICES_CUSTOM_AUDIENCE
권한을 포함합니다.<uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" />
매니페스트의
<application>
요소에서 광고 서비스 구성을 참조합니다.<property android:name="android.adservices.AD_SERVICES_CONFIG" android:resource="@xml/ad_services_config" />
매니페스트에 참조된 광고 서비스 XML 리소스를 지정합니다(예:
res/xml/ad_services_config.xml
). 광고 서비스 권한 및 SDK 액세스 제어에 관해 자세히 알아보세요.<ad-services-config> <custom-audiences allowAllToAccess="true" /> </ad-services-config>
기본적으로 Ad Selection API는 입찰 또는 노출 보고 스크립트에서 할당할 수 있는 최대 메모리 양을 제한합니다. 메모리 제한 기능을 사용하려면 WebView 버전 105.0.5195.58 이상이 필요합니다. 플랫폼은 버전 확인을 시행하고 조건이 충족되지 않으면
selectAds
및reportImpression
API 호출이 실패합니다. 이를 설정하는 옵션에는 두 가지가 있습니다.옵션 1: 다음 adb 명령어를 실행하여 버전 확인을 비활성화합니다.
adb device_config put fledge_js_isolate_enforce_max_heap_size false
옵션 2: Google Play 스토어에서 WebView 베타를 설치합니다. 이는 앞에서 언급된 버전 이상이어야 합니다.
맞춤 잠재고객에 참여
맞춤 잠재고객은 광고주 앱에 의해 결정되는, 공통의 의도나 관심분야가 있는 사용자 그룹을 나타냅니다. 앱 또는 SDK는 맞춤 잠재고객을 사용하여 장바구니에 상품이 남아 있는 사용자와 같은 특정 잠재고객을 나타낼 수 있습니다. 맞춤 잠재고객을 비동기식으로 만들거나 맞춤 잠재고객에 참여하려면 다음을 수행하세요.
CustomAudienceManager
객체를 초기화합니다.- 구매자의 패키지와 관련 이름과 같은 주요 매개변수를 지정하여
CustomAudience
객체를 만듭니다. 그런 다음JoinCustomAudienceRequest
객체를CustomAudience
객체로 초기화합니다. JoinCustomAudienceRequest
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기joinCustomAudience()
를 호출합니다.
Kotlin
val customAudienceManager: CustomAudienceManager =
context.getSystemService(CustomAudienceManager::class.java)
// Initialize a custom audience.
val audience = CustomAudience.Builder()
.setBuyer(buyer)
.setName(name)
...
.build()
// Initialize a custom audience request.
val joinCustomAudienceRequest: JoinCustomAudienceRequest =
JoinCustomAudienceRequest.Builder().setCustomAudience(audience).build()
// Request to join a custom audience.
customAudienceManager.joinCustomAudience(joinCustomAudienceRequest,
executor,
outcomeReceiver)
Java
CustomAudienceManager customAudienceManager =
context.getSystemService(CustomAudienceManager.class);
// Initialize a custom audience.
CustomAudience audience = new CustomAudience.Builder()
.setBuyer(buyer)
.setName(name)
...
.build();
// Initialize a custom audience request.
JoinCustomAudienceRequest joinCustomAudienceRequest =
new JoinCustomAudienceRequest.Builder().setCustomAudience(audience).build();
// Request to join a custom audience.
customAudienceManager.joinCustomAudience(joinCustomAudienceRequest,
executor,
outcomeReceiver);
다음 매개변수의 조합은 기기의 각 CustomAudience
객체를 고유하게 식별합니다.
owner
: 소유자 앱의 패키지 이름입니다. 암시적으로 호출자 앱의 패키지 이름으로 설정됩니다.buyer
: 이 맞춤 잠재고객의 광고를 관리하는 구매자 광고 네트워크의 식별자입니다.name
: 맞춤 잠재고객의 임의 이름 또는 식별자입니다.
CustomAudience
의 다른 인스턴스를 사용하여 joinCustomAudience()
를 반복적으로 호출하면 일치하는 owner, buyer
및 name
매개변수와 함께 기존 CustomAudience
가 업데이트됩니다. 개인 정보 보호를 유지하기 위해 API의 결과는 '생성'과 '업데이트'를 구분하지 않습니다.
또한 CustomAudience
는 다음 필수 매개변수를 사용하여 만들어야 합니다.
- 일일 업데이트 URL: 맞춤 잠재고객의 사용자 입찰 신호, 신뢰할 수 있는 입찰 데이터, 렌더링 URL 및 광고 메타데이터를 업데이트하기 위해 백그라운드에서 매일 쿼리되는 HTTPS URL입니다.
- 입찰 로직 URL: 광고 선택 중에 구매자의 JavaScript 입찰 로직을 가져오기 위해 쿼리되는 HTTPS URL입니다. 이 JavaScript에서 필수 함수 서명을 참고하세요.
- 광고 렌더링 ID: 구매자 광고 기술에서 설정한 임의의 ID입니다. 이는 B&A용 페이로드 생성을 위한 최적화입니다.
CustomAudience
객체의 선택적 매개변수는 다음과 같습니다.
- 활성화 시간: 맞춤 잠재고객은 활성화 시간 이후에만 광고 선택과 일일 업데이트에 참여할 수 있습니다. 이는 예를 들어 앱 사용을 중단한 사용자의 참여를 유도하는 데 유용할 수 있습니다.
- 만료 시간: 맞춤 잠재고객이 기기에서 삭제되기까지 남은 미래 시간입니다.
- 사용자 입찰 신호: 광고 선택 프로세스에서 구매자의 입찰 로직 JavaScript가 입찰 생성에 사용하는 사용자 신호(예: 사용자의 선호 언어)가 포함된 JSON 문자열입니다. 이 형식은 광고 기술 플랫폼이 여러 플랫폼 간의 코드를 재사용하는 데 도움이 되고 JavaScript 함수 사용을 용이하게 해 줍니다.
- 신뢰할 수 있는 입찰 데이터: 광고 선택 프로세스 중에 신뢰할 수 있는 키/값 서비스에서 입찰 신호를 가져오는 데 사용되는 HTTPS URL 및 문자열 목록입니다.
- 광고: 광고 선택에 참여할 광고에 해당하는
AdData
객체의 목록입니다. 각AdData
객체는 다음으로 구성됩니다.- 렌더링 URL: 최종 광고를 렌더링하기 위해 쿼리되는 HTTPS URL입니다.
- 메타데이터: 광고 선택 프로세스 중에 구매자 입찰 로직에 사용할 정보가 포함된 JSON 객체(문자열로 직렬화됨)입니다.
- 광고 필터: 광고 선택 중에 앱 설치 광고 필터링 및 최대 게재빈도 설정에 필요한 모든 정보가 포함된 클래스입니다.
다음은 CustomAudience
객체 인스턴스화의 예입니다.
Kotlin
// Minimal initialization of a CustomAudience object
val customAudience: CustomAudience = CustomAudience.Builder()
.setBuyer(AdTechIdentifier.fromString("my.buyer.domain.name"))
.setName("example-custom-audience-name")
.setDailyUpdateUrl(Uri.parse("https://DAILY_UPDATE_URL"))
.setBiddingLogicUrl(Uri.parse("https://BIDDING_LOGIC_URL"))
.build()
Java
// Minimal initialization of a CustomAudience object
CustomAudience customAudience = CustomAudience.Builder()
.setBuyer(AdTechIdentifier.fromString("my.buyer.domain.name"))
.setName("example-custom-audience-name")
.setDailyUpdateUrl(Uri.parse("https://DAILY_UPDATE_URL"))
.setBiddingLogicUrl(Uri.parse("https://BIDDING_LOGIC_URL"))
.build();
joinCustomAudience() 결과 처리
비동기 joinCustomAudience()
메서드는 OutcomeReceiver
객체를 사용하여 API 호출의 결과를 알립니다.
onResult()
콜백은 맞춤 잠재고객을 성공적으로 생성했거나 업데이트했음을 나타냅니다.onError()
콜백은 두 가지 가능한 조건을 나타냅니다.JoinCustomAudienceRequest
가 잘못된 인수로 초기화되면AdServicesException
은IllegalArgumentException
을 원인으로 표시합니다.- 그 외 모든 오류에서는
IllegalStateException
이 원인으로 표시된AdServicesException
을 수신하게 됩니다.
다음은 joinCustomAudience()
의 결과를 처리하는 예입니다.
Kotlin
var callback: OutcomeReceiver<Void, AdServicesException> =
object : OutcomeReceiver<Void, AdServicesException> {
override fun onResult(result: Void) {
Log.i("CustomAudience", "Completed joinCustomAudience")
}
override fun onError(error: AdServicesException) {
// Handle error
Log.e("CustomAudience", "Error executing joinCustomAudience", error)
}
};
Java
OutcomeReceiver callback = new OutcomeReceiver<Void, AdServicesException>() {
@Override
public void onResult(@NonNull Void result) {
Log.i("CustomAudience", "Completed joinCustomAudience");
}
@Override
public void onError(@NonNull AdServicesException error) {
// Handle error
Log.e("CustomAudience", "Error executing joinCustomAudience", error);
}
};
맞춤 잠재고객 탈퇴
사용자가 지정된 맞춤 잠재고객의 비즈니스 기준을 더 이상 충족하지 않으면 앱 또는 SDK가 leaveCustomAudience()
를 호출하여 기기에서 맞춤 잠재고객을 삭제할 수 있습니다. 고유한 매개변수를 기반으로 CustomAudience
를 삭제하려면 다음을 실행합니다.
CustomAudienceManager
객체를 초기화합니다.- 맞춤 잠재고객의
buyer
및name
을 사용하여LeaveCustomAudienceRequest
를 초기화합니다. 이러한 입력 필드에 관한 자세한 내용은 '맞춤 잠재고객에 참여'를 참고하세요. LeaveCustomAudienceRequest
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기leaveCustomAudience()
메서드를 호출합니다.
Kotlin
val customAudienceManager: CustomAudienceManager =
context.getSystemService(CustomAudienceManager::class.java)
// Initialize a LeaveCustomAudienceRequest
val leaveCustomAudienceRequest: LeaveCustomAudienceRequest =
LeaveCustomAudienceRequest.Builder()
.setBuyer(buyer)
.setName(name)
.build()
// Request to leave a custom audience
customAudienceManager.leaveCustomAudience(
leaveCustomAudienceRequest,
executor,
outcomeReceiver)
Java
CustomAudienceManager customAudienceManager =
context.getSystemService(CustomAudienceManager.class);
// Initialize a LeaveCustomAudienceRequest
LeaveCustomAudienceRequest leaveCustomAudienceRequest =
new LeaveCustomAudienceRequest.Builder()
.setBuyer(buyer)
.setName(name)
.build();
// Request to leave a custom audience
customAudienceManager.leaveCustomAudience(
leaveCustomAudienceRequest,
executor,
outcomeReceiver);
joinCustomAudience()
호출과 마찬가지로 OutcomeReceiver
는 API 호출 종료를 알립니다. 개인 정보 보호를 위해 오류 결과는 내부 오류와 잘못된 인수를 구분하지 않습니다. onResult()
콜백은 일치하는 맞춤 잠재고객이 제대로 삭제되었는지에 상관없이 API 호출이 완료되면 호출됩니다.
광고 선택 실행
Protected Audience API를 사용하여 광고를 선택하려면 selectAds()
메서드를 호출합니다.
AdSelectionManager
객체를 초기화합니다.AdSelectionConfig
객체를 빌드합니다.AdSelectionConfig
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기selectAds()
메서드를 호출합니다.
Kotlin
val adSelectionManager: AdSelectionManager =
context.getSystemService(AdSelectionManager::class.java)
// Initialize AdSelectionConfig
val adSelectionConfig: AdSelectionConfig =
AdSelectionConfig.Builder().setSeller(seller)
.setDecisionLogicUrl(decisionLogicUrl)
.setCustomAudienceBuyers(customAudienceBuyers)
.setAdSelectionSignals(adSelectionSignals)
.setSellerSignals(sellerSignals)
.setPerBuyerSignals(perBuyerSignals)
.setBuyerContextualAds(
Collections.singletonMap(
contextualAds.getBuyer(), contextualAds
)
).build()
// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
adSelectionConfig, executor, outcomeReceiver
)
Java
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
// Initialize AdSelectionConfig
AdSelectionConfig adSelectionConfig =
new AdSelectionConfig.Builder()
.setSeller(seller)
.setDecisionLogicUrl(decisionLogicUrl)
.setCustomAudienceBuyers(customAudienceBuyers)
.setAdSelectionSignals(adSelectionSignals)
.setSellerSignals(sellerSignals)
.setPerBuyerSignals(perBuyerSignals)
.setBuyerContextualAds(
Collections.singletonMap(contextualAds.getBuyer(), contextualAds)
)
.build();
// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(adSelectionConfig, executor, outcomeReceiver);
selectAds()
메서드에는 다음 필수 매개변수를 지정해야 하는 AdSelectionConfig
입력이 필요합니다.
- 판매자: 광고 선택을 시작하는 판매자 광고 네트워크의 식별자입니다.
- 결정 로직 URL: 판매자 광고 네트워크의 JavaScript 로직을 가져오기 위해 쿼리되는 HTTPS URL입니다.
- HTTPS URL: 판매자 광고 네트워크의 JavaScript 로직을 가져오기 위해 쿼리됩니다. 필수 함수 서명을 참고하세요.
- 사전 빌드된 URI: FLEDGE의 광고 선택 형식을 따릅니다.
지원되지 않거나 형식이 잘못된 사전 빌드된 URI가 전달되면
IllegalArgumentException
이 발생합니다.
- 맞춤 잠재고객 구매자: 판매자가 광고 선택 프로세스에 참여할 수 있도록 허용한 구매자 광고 네트워크의 전체 식별자 목록입니다.
이러한 구매자 식별자는 참여하는 맞춤 잠재고객의
CustomAudience.getBuyer()
에 상응합니다.
원하는 경우 다음 매개변수를 지정하여 광고 선택을 더욱 맞춤설정할 수 있습니다.
- 광고 선택 신호:
CustomAudience.getBiddingLogicUrl()
에서 가져온 구매자 입찰 로직 JavaScript에 사용할 신호를 포함하는 JSON 객체(문자열로 직렬화됨)입니다. - 판매자 신호: 판매자가
AdSelectionConfig.getDecisionLogicUrl()
에서 가져온 JavaScript 결정 로직에 사용되는 신호가 포함된 JSON 객체(문자열로 직렬화됨)입니다. - 구매자 신호별:
CustomAudience.getBiddingLogicUrl()
에서 가져온 특정 구매자의 입찰 로직 JavaScript에 사용되는 신호가 포함된 JSON 객체(문자열로 직렬화됨)의 맵입니다. 이러한 객체는 참여하는 맞춤 잠재고객의 구매자 필드를 통해 식별됩니다. - 문맥 광고: Protected Audience 입찰 외부에서 발생하는 입찰 중에 구매자로부터 직접 수집되는 광고 조합의 컬렉션입니다.
광고가 선택되면 결과, 입찰 및 신호는 보고 목적으로 내부적으로 유지됩니다. OutcomeReceiver.onResult()
콜백은 다음을 포함하는 AdSelectionOutcome
을 반환합니다.
AdData.getRenderUrl()
에서 가져온 낙찰 광고의 렌더링 URL- 기기 사용자의 고유한 광고 선택 ID. 이 ID는 광고 노출을 보고하는 데 사용됩니다.
잘못된 인수, 시간 초과, 과도한 리소스 사용 같은 이유로 인해 광고 선택을 제대로 완료할 수 없는 경우 OutcomeReceiver.onError()
콜백이 다음 동작과 함께 AdServicesException
을 제공합니다.
- 잘못된 인수로 광고 선택이 시작되면
AdServicesException
은IllegalArgumentException
을 원인으로 나타냅니다. - 그 외 모든 오류에서는
IllegalStateException
이 원인으로 표시된AdServicesException
을 수신하게 됩니다.
문맥 광고
Protected Audience는 문맥 광고를 Protected Auction에 통합할 수 있습니다.
문맥 광고는 광고 기술 서버에서 선택하고 서명하여 Protected Audience API 외부의 기기로 반환해야 합니다. 그러면 문맥 광고는 AdSelectionConfig
를 사용하여 입찰에 포함될 수 있으며 이때 서명이 인증되면 제외 광고 필터링 자격 요건 등 기기 내 광고와 동일하게 작동합니다. Protected Audience 입찰이 완료되면 reportImpression()
을 호출해야 합니다. 이렇게 하면 낙찰된 문맥 광고에서 노출 보고와 동일한 패턴으로 reportWin()
을 호출하여 기기에서 낙찰된 광고를 수신합니다. 각 문맥 광고에는 구매자, 입찰가, 보고 로직 링크, 렌더링 URL, 광고 메타데이터가 필요합니다.
앱에 문맥 광고를 배포하려면 타겟 앱에서 SignedContextualAds
객체를 만들어야 합니다.
Kotlin
val signedContextualAds: SignedContextualAds =
Builder().setBuyer(AdTechIdentifier.fromString(mBiddingLogicUri.getHost()))
//Pass in your valid app install ads
.setDecisionLogicUri(mContextualLogicUri)
.setAdsWithBid(appInstallAd)
.setSignature(signature)
.build()
Java
SignedContextualAds signedContextualAds = new SignedContextualAds.Builder()
.setBuyer(AdTechIdentifier.fromString(mBiddingLogicUri.getHost()))
.setDecisionLogicUri(mContextualLogicUri)
//Pass in your valid app install ads
.setAdsWithBid(appInstallAd)
.setSignature(signature)
.build();
그런 다음 AdSelectionConfig
를 만들 때 결과 SignedContextualAds
객체를 함께 전달할 수 있습니다.
Kotlin
val adSelectionConfig = AdSelectionConfig.Builder()
.setPerBuyerSignedContextualAds(signedContextualAds)
// Other fields for your config
.build()
Java
AdSelectionConfig adSelectionConfig = new AdSelectionConfig.Builder()
.setPerBuyerSignedContextualAds(signedContextualAds)
//Other fields for your config
.build();
앱 설치 광고 필터링
앱 설치 광고 필터링을 사용하면 기기에 이미 설치된 앱의 설치 광고를 필터링할 수 있습니다.
이 프로세스의 첫 번째 단계는 설치된 패키지를 필터링할 수 있는 광고주를 정의하는 것입니다. 이 단계는 광고로 타겟팅하려는 앱에서 진행되어야 합니다.
Kotlin
//Create a request for setting the app install advertisers
val adtech = AdTechIdentifier.fromString("your.enrolled.uri")
val adtechSet = setOf(adtech)
val request = SetAppInstallAdvertisersRequest(adtechSet)
//Set the app install advertisers in the ad selection manager
mAdSelectionManager.setAppInstallAdvertisers(
request,
mExecutor,
object : OutcomeReceiver<Any?, Exception?>() {
fun onResult(@NonNull ignoredResult: Any?) {
Log.v("[your tag]", "Updated app install advertisers")
}
fun onError(@NonNull error: Exception?) {
Log.e("[your tag]", "Failed to update app install advertisers", error)
}
})
Java
//Create a request for setting the app install advertisers
AdTechIdentifier adtech = AdTechIdentifier.fromString("your.enrolled.uri");
Set<AdTechIdentifier> adtechSet = Collections.singleton(adtech);
SetAppInstallAdvertisersRequest request = new SetAppInstallAdvertisersRequest(adtechSet);
//Set the app install advertisers in the ad selection manager
mAdSelectionManager.setAppInstallAdvertisers(
request,
mExecutor,
new OutcomeReceiver<Object, Exception>() {
@Override
public void onResult(@NonNull Object ignoredResult) {
Log.v("[your tag]", "Updated app install advertisers");
}
@Override
public void onError(@NonNull Exception error) {
Log.e("[your tag]", "Failed to update app install advertisers", error);
}
});
위 코드가 실행되면 전달된 광고주는 입찰 생성 중에 지정된 설치된 앱을 필터링할 수 있습니다. 이 앱의 설치 상태에 액세스할 수 없도록 특정 광고주를 삭제해야 하는 경우 광고주 정보를 삭제한 상태로 이 코드를 다시 실행하세요.
다음 단계는 게시자 앱 내에서 광고 필터링을 설정하는 것입니다. 게시자 앱 내부에 광고를 게재하는 당사자(공급 측 SDK일 가능성이 높음)는 필터링하려는 앱과 관련된 광고에 관한 정보로 AdFilters
객체를 초기화해야 합니다.
Kotlin
// Instantiate AdFilters object with package names.
val filters: AdFilters = Builder().setAppInstallFilters(
Builder().setPackageNames(setOf("example.target.app")).build()
).build()
Java
// Instantiate AdFilters object with package names.
AdFilters filters = new AdFilters.Builder()
.setAppInstallFilters(
new AppInstallFilters.Builder()
.setPackageNames(Collections.singleton("example.target.app"))
.build())
.build();
수요 측 게시자는 맞춤 잠재고객 내에 있는 광고에 AdFilter
를 설정할 수도 있습니다.
새 AdData
객체를 인스턴스화하는 시점에 AdFilters
도 전달할 수 있습니다.
Kotlin
// Instantiate an AdData object with the AdFilters created in the
// previous example.
val appInstallAd: AdData =
Builder().setMetadata("{ ... }") // Valid JSON string
.setRenderUri(Uri.parse("www.example-dsp1.com/.../campaign123.html"))
.setAdFilters(filters).build()
Java
// Instantiate an AdData object with the AdFilters created in the
// previous example.
AdData appInstallAd = new AdData.Builder()
.setMetadata("{ ... }") // Valid JSON string
.setRenderUri(Uri.parse("www.example-dsp1.com/.../campaign123.html"))
.setAdFilters(filters)
.build();
최대 게재빈도 필터링
최대 게재빈도 필터링을 사용하면 광고 기술이 광고가 표시되는 횟수를 제한할 수 있습니다. 최대 게재빈도 필터링은 광고 과다 노출을 줄이고 특정 광고 캠페인의 대체 광고 선택을 최적화합니다.
최대 게재빈도 필터에는 광고 이벤트 유형과 광고 카운터 키라는 두 가지 주요 구성요소가 있습니다. 사용할 수 있는 광고 이벤트 유형은 다음과 같습니다.
- 낙찰: 낙찰 이벤트는 광고가 낙찰되었음을 나타냅니다. 낙찰 이벤트는 Protected Audience API에 의해 자동으로 업데이트되며 개발자가 직접 호출할 수 없습니다. 낙찰 데이터는 특정 맞춤 잠재고객 내의 광고에만 표시됩니다.
- 노출:
reportImpression
과는 별도로 기기 내 호출자(SSP 또는 MMP)는updateAdCounterHistogram()
을 사용하여, 선택한 코드의 특정 지점에서 노출 이벤트를 호출합니다. 노출 이벤트는 특정 DSP에 속한 모든 광고에 표시되며, 동일한 맞춤 잠재고객의 광고로 제한되지 않습니다. - 조회: 이벤트는
updateAdCounterHistogram()
호출을 통해 선택한 코드의 지점에서 기기 내 호출자(SSP 또는 MMP)에 의해 호출됩니다. 조회 이벤트는 특정 DSP에 속한 모든 광고에 표시되며, 동일한 맞춤 잠재고객의 광고로 제한되지 않습니다. - 클릭:
updateAdCounterHistogram()
호출을 사용하여 선택한 코드의 지점에서 기기 내 호출자(SSP 또는 MMP)에 의해 이벤트가 호출됩니다. 클릭 이벤트는 지정된 DSP에 속한 모든 광고에 표시되며, 동일한 맞춤 잠재고객의 광고로 제한되지 않습니다.
게시자 앱에서 기기에 있는 SSP 또는 MMP가 광고 이벤트를 호출합니다. updateAdCounterHistogram()
이 호출되면 최대 게재빈도 필터의 카운터가 증가하여 향후 입찰에서 특정 광고에 대한 사용자의 노출 관련 정보가 최신 상태로 유지됩니다. 광고 이벤트 유형은 해당 사용자 작업에 강제로 연결되지 않으며, 호출자가 이벤트 시스템을 구조화하는 데 도움이 되는 가이드라인입니다. 이벤트 발생 시 광고 카운터를 증가시키기 위해 기기 내 행위자는 낙찰된 광고 입찰의 광고 선택 ID를 제공합니다.
광고 카운터 키는 구매자 광고 기술에서 할당한 임의의 32비트 부호 있는 정수이며 DSP에서 정의한 바와 같이 지정된 광고 집합에 해당합니다. 광고 카운터 키는 특정 DSP에 속한 광고로만 제한되므로 다른 광고 기술의 히스토그램과 겹치지 않고 이러한 키를 선택할 수 있습니다. 광고 카운터 키는 DSP의 광고 전체에서 또는 지정된 맞춤 잠재고객 내에서 DSP 관련 식별자를 증가시켜 향후 입찰에서 광고를 필터링하는 데 사용됩니다.
카운터 키를 활용하여 특정 구매자 광고 기술의 다른 광고와의 상호작용을 기반으로 특정 사용자가 관심을 보일 만한 광고의 우선순위를 정할 수 있습니다. 예를 들어 낙찰된 광고 입찰, 조회, 클릭에서 높은 수준의 참여가 발생한 광고는 추론된 데이터 포인트를 나타냅니다. 좀 더 자세히 설명하면 왼손잡이용 골프채에 대한 광고는 사용자가 오른손잡이용 골프채에 관심이 없다는 것을 나타낼 수 있습니다. 왼손잡이 광고에 할당된 카운터 키에 최대 게재빈도 필터를 설정하면 오른손잡이용 골프채의 광고가 필터링될 수 있습니다.
입찰에서 최대 게재빈도 설정을 사용하려면 먼저 아래와 같이 KeyedFrequencyCap
객체를 만들어야 합니다.
Kotlin
// Value used when incrementing frequency counter
val adCounterKey = 123
// Frequency cap exceeded after 2 counts
val keyedFrequencyCapForImpression: KeyedFrequencyCap = Builder(
adCounterKey, 2, Duration.ofSeconds(10)
).build()
// Frequency cap exceeded after 1 counts
val keyedFrequencyCapForImpression: KeyedFrequencyCap = Builder(
adCounterKey, 1, Duration.ofSeconds(10)
).build()
Java
// Value used when incrementing frequency counter
int adCounterKey = 123;
// Frequency cap exceeded after 2 counts
KeyedFrequencyCap keyedFrequencyCapForImpression =
new KeyedFrequencyCap.Builder(
adCounterKey, 2, Duration.ofSeconds(10)
).build();
// Frequency Cap exceeded after 1 counts
KeyedFrequencyCap keyedFrequencyCapForClick =
new KeyedFrequencyCap.Builder(
adCounterKey, 1, Duration.ofSeconds(10)
).build();
KeyedFrequencyCap
객체를 만든 후 AdFilters
객체에 전달할 수 있습니다.
Kotlin
val filters: AdFilters = Builder()
.setFrequencyCapFilters(
Builder()
.setKeyedFrequencyCapsForImpressionEvents(
ImmutableObject.of(keyedFrequencyCapForImpression)
)
.setKeyedFrequencyCapsForClickEvents(
ImmutableObject.of(keyedFrequencyCapForClick)
)
).build()
Java
AdFilters filters = new AdFilters.Builder()
.setFrequencyCapFilters(new FrequencyCapFilters.Builder()
.setKeyedFrequencyCapsForImpressionEvents(
ImmutableObject.of(keyedFrequencyCapForImpression)
)
.setKeyedFrequencyCapsForClickEvents(
ImmutableObject.of(keyedFrequencyCapForClick)
)
).build();
AdFilters
객체가 최대 게재빈도 필터로 채워지면 맞춤 잠재고객이 생성될 때 함께 전달될 수 있습니다.
Kotlin
// Initialize a custom audience.
val audience: CustomAudience = Builder()
.setBuyer(buyer)
.setName(name)
.setAds(
listOf(
Builder()
.setRenderUri(renderUri)
.setMetadata(JSONObject().toString())
.setAdFilters(filters)
.setAdCounterKeys(adCounterKeys)
.build()
)
).build()
Java
// Initialize a custom audience.
CustomAudience audience = new CustomAudience.Builder()
.setBuyer(buyer)
.setName(name)
.setAds(Collections.singletonList(new AdData.Builder()
.setRenderUri(renderUri)
.setMetadata(new JSONObject().toString())
.setAdFilters(filters)
.setAdCounterKeys(adCounterKeys)
.build()))
.build();
최대 게재빈도 필터가 맞춤 잠재고객에 구현되면 SSP는 필요한 클릭, 조회 또는 노출 이벤트를 호출할 수 있습니다.
Kotlin
val callerAdTech: AdTechIdentifier = mAdSelectionConfig.getSeller()
val request: UpdateAdCounterHistogramRequest = Builder(
adSelectionId,
FrequencyCapFilters.AD_EVENT_TYPE_CLICK, //CLICK, VIEW, or IMPRESSION
callerAdTech
).build()
Java
AdTechIdentifier callerAdTech = mAdSelectionConfig.getSeller();
UpdateAdCounterHistogramRequest request =
new UpdateAdCounterHistogramRequest.Builder(
adSelectionId,
FrequencyCapFilters.AD_EVENT_TYPE_CLICK, //CLICK, VIEW, or IMPRESSION
callerAdTech
).build();
사전 설정된 최대 게재빈도 필터 한도에 도달한 광고는 입찰에서 필터링됩니다. 필터링은 입찰 로직이 기기 내 입찰에 대해 실행되기 전에, 그리고 입찰 및 입찰 서비스 입찰을 위해 페이로드가 생성될 때 발생합니다. 이 툴킷은 광고 기술이 맞춤 잠재고객 내에서 사용자와 입찰 서비스 간의 상호작용을 활용할 수 있는 유연성을 제공합니다. 광고 과다 노출을 최소화하면서 광고 타겟팅에 집중할 수 있습니다.
서명을 만드는 방법
서명 알고리즘 및 키 생성
ECDSA 알고리즘을 사용하여 광고에 서명 키 유형은 P-256 ECDSA('secp256r1' 또는 'prime256v1')여야 합니다. 다음은 다양한 언어로 서명하는 방법을 보여주는 예입니다.
Java
import java.security.*;
public class ECDSASignatureManager {
private static KeyPair generateKeyPair() throws Exception {
// Specify the curve name P-256
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
// Initialize the KeyPairGenerator
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(ecSpec);
// Generate the KeyPair
return keyPairGenerator.generateKeyPair();
}
public byte[] sign(byte[] data, byte[] privateKey) throws Exception {
Signature ecdsa = Signature.getInstance("SHA256withECDSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PrivateKey aPrivateKey = keyFactory.generatePrivate(keySpec);
ecdsa.initSign(aPrivateKey);
ecdsa.update(data);
return ecdsa.sign();
}
}
JavaScript
const crypto = require('crypto');
function generateKeyPair() {
const keyPair = crypto.generateKeyPairSync('ec', {
// Specifies ECDSA algorithm
namedCurve: 'prime256v1',
publicKeyEncoding: { type: 'spki', format: 'der' },
privateKeyEncoding: { type: 'pkcs8', format: 'der'}
});
return {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey
};
}
function signContextualAds(data, privateKey) {
// Convert the private key from PKCS8 to PEM format
const base64PrivateKey = Buffer.from(privateKey).toString('base64');
const pemPrivateKey = `-----BEGIN PRIVATE KEY-----\n${base64PrivateKey}\n-----END PRIVATE KEY-----`;
// Create a Sign object using SHA256
const sign = crypto.createSign('SHA256');
sign.update(Buffer.from(data));
// Sign the data with ECDSA as specified in the key specs
return sign.sign(pemPrivateKey);
}
Python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
def sign_contextual_ads(data, private_key_der):
private_key = serialization.load_der_private_key(
private_key_der,
password=None,
backend=default_backend()
)
signature = private_key.sign(
data,
ec.ECDSA(hashes.SHA256())
)
return signature
def generate_key_pair():
private_key = ec.generate_private_key(ec.SECP256R1())
# Serialize the keys into DER format
public_key_der = private_key.public_key().public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
private_key_der = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
return public_key_der, private_key_der
직렬화
서명은 구매자의 서버에서 생성되므로 시스템 간에 지정된 SignedContextualAds
의 직렬화를 일관되게 유지하려면 일련의 표준 규칙이 필요합니다.
표준 규칙
일련의 규칙을 통해 직렬화가 광고 기술과 Protected Audience 간에 일관되게 유지될 수 있습니다. 이러한 규칙은 더 쉽게 채택할 수 있도록 다양한 언어의 코드 스니펫과 함께 개발자 가이드에 있습니다.
- 객체는 필드의 직렬화된 값을 각 필드 다음에 오는 '|'(파이프)와 연결합니다. 마지막 필드에도 편의상 '|'가 표시됩니다. 'field_name_1=value1|field_name_2=value2|'
- 객체 필드는 스네이크 표기법의 필드 이름, 등호, 직렬화된 필드 값을 연결하여 직렬화됩니다. 'my_field=
|' - 모든 필드는 객체 내 필드 이름의 알파벳순으로 정렬됩니다.
- Null 허용(선택사항) 필드는 null이거나 설정되지 않은 경우 건너뜁니다(비어 있음).
- Double은 소수점 이하 2자리 정밀도를 갖는 문자열로 변환됩니다.
- 정수는 문자열 값으로 변환됩니다.
- 세트는 알파벳순으로 정렬됩니다.
- 목록은 쉼표로 구분되어 동일한 순서를 유지합니다.
문자열은 UTF-8을 사용하여 byte[]로 인코딩됩니다.
Java
public class ContextualAdsSerializationUtil {
private static final String FIELD_SEPARATOR = "|";
private static final String ENUMERATOR_SEPARATOR = ",";
private final ByteArrayOutputStream mByteArrayStream;
public ContextualAdsSerializationUtil() {
this.mByteArrayStream = new ByteArrayOutputStream();
}
byte[] getBytes() {
return this.mByteArrayStream.toByteArray();
}
public byte[] serialize(ContextualAds ads) {
writeContextualAds(ads);
return getBytes();
}
private void writeContextualAds(ContextualAds ads) {
writeString("buyer=");
writeField(ads.getBuyer().getIdentifier(), this::writeString);
writeString("decision_logic_uri=");
writeField(ads.getDecisionLogicUri().toString(), this::writeString);
writeString("ads_with_bid=");
writeList(ads.getAdsWithBid(), this::writeAdWithBid);
}
private void writeAdWithBid(AdWithBid adWithBid) {
writeString("ad_data=");
writeField(adWithBid.getAdData(), this::writeAdData);
writeString("bid=");
writeField(adWithBid.getBid(), this::writeDouble);
}
private void writeAdData(AdData adData) {
writeString("ad_counter_keys=");
writeSet(adData.getAdCounterKeys(), this::writeInt);
if (!Objects.isNull(adData.getAdFilters())) {
writeString("ad_filters=");
writeField(adData.getAdFilters(), this::writeAdFilters);
}
if (!Objects.isNull(adData.getAdRenderId())) {
writeString("ad_render_id=");
writeField(adData.getAdRenderId(), this::writeString);
}
writeString("metadata=");
writeField(adData.getMetadata(), this::writeString);
writeString("render_uri=");
writeField(adData.getRenderUri().toString(), this::writeString);
}
private void writeAdFilters(AdFilters adFilters) {
if (!Objects.isNull(adFilters.getAppInstallFilters())) {
writeString("app_install_filters=");
writeField(adFilters.getAppInstallFilters(), this::writeAppInstallFilter);
}
if (!Objects.isNull(adFilters.getFrequencyCapFilters())) {
writeString("frequency_cap_filters=");
writeField(adFilters.getFrequencyCapFilters(), this::writeFrequencyCapFilters);
}
}
private void writeAppInstallFilter(AppInstallFilters appInstallFilters) {
writeString("package_names=");
writeSet(appInstallFilters.getPackageNames(), this::writeString);
}
private void writeFrequencyCapFilters(FrequencyCapFilters frequencyCapFilters) {
writeString("keyed_frequency_caps_for_click_events=");
writeList(
frequencyCapFilters.getKeyedFrequencyCapsForClickEvents(),
this::writeKeyedFrequencyCap);
writeString("keyed_frequency_caps_for_impression_events=");
writeList(
frequencyCapFilters.getKeyedFrequencyCapsForImpressionEvents(),
this::writeKeyedFrequencyCap);
writeString("keyed_frequency_caps_for_view_events=");
writeList(
frequencyCapFilters.getKeyedFrequencyCapsForViewEvents(),
this::writeKeyedFrequencyCap);
writeString("keyed_frequency_caps_for_win_events=");
writeList(
frequencyCapFilters.getKeyedFrequencyCapsForWinEvents(),
this::writeKeyedFrequencyCap);
}
private void writeKeyedFrequencyCap(KeyedFrequencyCap keyedFrequencyCap) {
writeString("ad_counter_key=");
writeField(keyedFrequencyCap.getAdCounterKey(), this::writeInt);
writeString("interval=");
writeField(keyedFrequencyCap.getInterval().toMillis(), this::writeLong);
writeString("max_count=");
writeField(keyedFrequencyCap.getMaxCount(), this::writeInt);
}
private <T> void writeField(T field, Consumer<T> writerConsumer) {
Objects.requireNonNull(field);
writerConsumer.accept(field);
writeString(FIELD_SEPARATOR);
}
private <T> void writeList(List<T> list, Consumer<T> writerConsumer) {
FirstElementStateHolder state = new FirstElementStateHolder();
for (T t : list) {
if (!state.isFirstThenSetFalse()) {
writeString(ENUMERATOR_SEPARATOR);
}
writerConsumer.accept(t);
}
writeString(FIELD_SEPARATOR);
}
private <T> void writeSet(Set<T> set, Consumer<T> writerConsumer) {
FirstElementStateHolder state = new FirstElementStateHolder();
set.stream()
.sorted()
.forEach(
(value) -> {
if (!state.isFirstThenSetFalse()) {
writeString(ENUMERATOR_SEPARATOR);
}
writerConsumer.accept(value);
});
writeString(FIELD_SEPARATOR);
}
void writeString(String value) {
mByteArrayStream.writeBytes(value.getBytes(StandardCharsets.UTF_8));
}
void writeLong(long value) {
writeString(Long.toString(value));
}
void writeDouble(double value) {
writeString(String.format("%.2f", value));
}
void writeInt(int value) {
writeString(Integer.toString(value));
}
private static class FirstElementStateHolder {
private boolean mIsFirst = true;
public boolean isFirstThenSetFalse() {
boolean initialValue = mIsFirst;
mIsFirst = false;
return initialValue;
}
}
}
JavaScript
class SignedContextualAdsHashUtil {
constructor() {
this.serializedString = '';
}
serialize(ads) {
this.writeContextualAds(ads);
return new TextEncoder().encode(this.serializedString);
}
writeContextualAds(ads) {
this.writeString('buyer=');
this.writeField(ads.buyer.identifier, value => this.writeString(value));
this.writeString('decision_logic_uri=');
this.writeField(ads.decisionLogicUri, value => this.writeString(value));
this.writeString('ads_with_bid=');
this.writeList(ads.adsWithBid, adWithBid => this.writeAdWithBid(adWithBid));
}
writeAdWithBid(adWithBid) {
this.writeString('ad_data=');
this.writeField(adWithBid.adData, adData => this.writeAdData(adData));
this.writeString('bid=');
this.writeField(adWithBid.bid, value => this.writeDouble(value));
}
writeAdData(adData) {
this.writeString('ad_counter_keys=');
this.writeSet([...adData.adCounterKeys], value => this.writeInt(value));
if (adData.adFilters) {
this.writeString('ad_filters=');
this.writeField(adData.adFilters, adFilters => this.writeAdFilters(adFilters));
}
if (adData.adRenderId) {
this.writeString('ad_render_id=');
this.writeField(adData.adRenderId, value => this.writeString(value));
}
this.writeString('metadata=');
this.writeField(adData.metadata, value => this.writeString(value));
this.writeString('render_uri=');
this.writeField(adData.renderUri, value => this.writeString(value));
}
writeAdFilters(adFilters) {
if (adFilters.appInstallFilters) {
this.writeString('app_install_filters=');
this.writeField(adFilters.appInstallFilters, appInstallFilters => this.writeAppInstallFilter(appInstallFilters));
}
if (adFilters.frequencyCapFilters) {
this.writeString('frequency_cap_filters=');
this.writeField(adFilters.frequencyCapFilters, frequencyCapFilters => this.writeFrequencyCapFilters(frequencyCapFilters));
}
}
writeAppInstallFilter(appInstallFilters) {
this.writeString('package_names=');
this.writeSet([...appInstallFilters.packageNames], value => this.writeString(value));
}
writeFrequencyCapFilters(frequencyCapFilters) {
this.writeString('keyed_frequency_caps_for_click_events=');
this.writeList(frequencyCapFilters.keyedFrequencyCapsForClickEvents, cap => this.writeKeyedFrequencyCap(cap));
this.writeString('keyed_frequency_caps_for_impression_events=');
this.writeList(frequencyCapFilters.keyedFrequencyCapsForImpressionEvents, cap => this.writeKeyedFrequencyCap(cap));
this.writeString('keyed_frequency_caps_for_view_events=');
this.writeList(frequencyCapFilters.keyedFrequencyCapsForViewEvents, cap => this.writeKeyedFrequencyCap(cap));
this.writeString('keyed_frequency_caps_for_win_events=');
this.writeList(frequencyCapFilters.keyedFrequencyCapsForWinEvents, cap => this.writeKeyedFrequencyCap(cap));
}
writeKeyedFrequencyCap(cap) {
this.writeString('ad_counter_key=');
this.writeField(cap.adCounterKey, value => this.writeInt(value));
this.writeString('interval=');
this.writeField(cap.interval, value => this.writeLong(value));
this.writeString('max_count=');
this.writeField(cap.maxCount, value => this.writeInt(value));
}
writeField(field, writer) {
writer(field);
this.writeString('|');
}
writeList(list, writer) {
list.forEach((item, index) => {
if (index > 0) this.writeString(',');
writer(item);
});
this.writeString('|');
}
writeSet(set, writer) {
[...set].sort().forEach((item, index) => {
if (index > 0) this.writeString(',');
writer(item);
});
this.writeString('|');
}
writeString(value) {
this.serializedString += value;
}
writeLong(value) {
this.writeString(value.toString());
}
writeDouble(value) {
this.writeString(value.toFixed(2));
}
writeInt(value) {
this.writeString(value.toString());
}
}
module.exports = {
SignedContextualAdsHashUtil,
};
Python
class SignedContextualAdsHashUtil:
def __init__(self):
self.serialized_string = ''
def serialize(self, ads):
self.write_contextual_ads(ads)
return self.serialized_string.encode('utf-8')
def write_contextual_ads(self, ads):
self.write_string('buyer=')
self.write_field(ads.buyer.identifier, lambda v: self.write_string(v))
self.write_string('decision_logic_uri=')
self.write_field(ads.decision_logic_uri, lambda v: self.write_string(v))
self.write_string('ads_with_bid=')
self.write_list(ads.ads_with_bid, self.write_ad_with_bid)
def write_ad_with_bid(self, ad_with_bid):
self.write_string('ad_data=')
self.write_field(ad_with_bid.ad_data, self.write_ad_data)
self.write_string('bid=')
self.write_field(ad_with_bid.bid, lambda v: self.write_double(v))
def write_ad_data(self, ad_data):
self.write_string('ad_counter_keys=')
self.write_set(ad_data.ad_counter_keys, lambda v: self.write_int(v))
if ad_data.ad_filters:
self.write_string('ad_filters=')
self.write_field(ad_data.ad_filters, self.write_ad_filters)
if ad_data.ad_render_id:
self.write_string('ad_render_id=')
self.write_field(ad_data.ad_render_id, lambda v: self.write_string(v))
self.write_string('metadata=')
self.write_field(ad_data.metadata, lambda v: self.write_string(v))
self.write_string('render_uri=')
self.write_field(ad_data.render_uri, lambda v: self.write_string(v))
def write_ad_filters(self, ad_filters):
if ad_filters.app_install_filters:
self.write_string('app_install_filters=')
self.write_field(ad_filters.app_install_filters,
self.write_app_install_filter)
if ad_filters.frequency_cap_filters:
self.write_string('frequency_cap_filters=')
self.write_field(ad_filters.frequency_cap_filters,
self.write_frequency_cap_filters)
def write_app_install_filter(self, app_install_filters):
self.write_string('package_names=')
self.write_set(app_install_filters.package_names,
lambda v: self.write_string(v))
def write_frequency_cap_filters(self, frequency_cap_filters):
for attr in ['keyed_frequency_caps_for_click_events',
'keyed_frequency_caps_for_impression_events',
'keyed_frequency_caps_for_view_events',
'keyed_frequency_caps_for_win_events']:
self.write_string(f'{attr}=')
self.write_list(getattr(frequency_cap_filters, attr),
self.write_keyed_frequency_cap)
def write_keyed_frequency_cap(self, cap):
self.write_string('ad_counter_key=')
self.write_field(cap.ad_counter_key, lambda v: self.write_int(v))
self.write_string('interval=')
self.write_field(cap.interval, lambda v: self.write_long(v))
self.write_string('max_count=')
self.write_field(cap.max_count, lambda v: self.write_int(v))
def write_field(self, field, writer):
writer(field)
self.write_string('|')
def write_list(self, list_items, writer):
for index, item in enumerate(list_items):
if index > 0:
self.write_string(',')
writer(item)
self.write_string('|')
def write_set(self, set_items, writer):
for index, item in enumerate(sorted(set_items)):
if index > 0:
self.write_string(',')
writer(item)
self.write_string('|')
def write_string(self, value):
self.serialized_string += value
def write_long(self, value):
self.write_string(str(value))
def write_double(self, value):
self.write_string(f'{value:.2f}')
def write_int(self, value):
self.write_string(str(value))
예
예 1: 모든 필드가 있는 문맥 광고 객체. 아래 문맥 광고 객체가 있다고 가정해 보겠습니다.
ContextualAds contextualAds = new ContextualAds.Builder()
.setBuyer(new AdTechIdentifier.Builder().setIdentifier("https://example.com").build()) // Replaced 'buyer' variable
.setDecisionLogicUri(URI.create("example.com/decisionLogic")) // Replaced 'decisionLogicUri'
.setAdsWithBid(
List.of(
new AdWithBid.Builder()
.setAdData(new AdData.Builder()
.setMetadata("metadata") // Replaced 'metadata'
.setAdFilters(buildAdFilters()) // Created a helper for filters
.setAdRenderId("987") // Replaced 'adRenderId'
.setAdCounterKeys(buildAdCounterKeys()) // Created a helper
.setRenderUri(URI.create("example.com/render")) // Replaced 'adRenderUri'
.build())
.setBid(5.10) // Replaced 'bid' (as double)
.build()))
.build();
// Helper function to build FrequencyCapFilters
private static FrequencyCapFilters buildAdFilters() {
KeyedFrequencyCap keyedFrequencyCap = new KeyedFrequencyCap.Builder()
.setAdCounterKey(42) // Replaced 'adCounterKey'
.setMaxCount(43) // Replaced 'maxCount'
.setInterval(Duration.ofDays(1)) // Replaced 'oneDay'
.build();
return new FrequencyCapFilters.Builder()
// ... set all KeyedFrequencyCaps using keyedFrequencyCap ...
.build();
}
// Helper function to convert adCounterKeys string
private static Set<Integer> buildAdCounterKeys() {
return Arrays.stream("1,2,3".split(",")) // Replaced 'adCounterKeys'
.map(Integer::valueOf)
.collect(Collectors.toSet());
}
직렬화된 byte[]의 문자열 표현은 다음과 같아야 합니다.
"buyer=https://example.com|decision_logic_uri=example.com/decisionLogic|ads_with_bid=ad_data=ad_counter_keys=1,2,3|ad_filters=app_install_filters=package_names=com.awesome.app1,com.awesome.app2||frequency_cap_filters=keyed_frequency_caps_for_click_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_impression_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_view_events=ad_counter_key=42|interval=86400000|max_count=43||keyed_frequency_caps_for_win_events=ad_counter_key=42|interval=86400000|max_count=43||||ad_render_id=987|metadata=metadata|render_uri=example.com/render||bid=5.10||"
예2: 필드가 비어 있는 문맥 광고 객체. 아래 객체에서 'adFilters'와 'adCounterKeys'는 비어 있으며 각각 'nullable'과 'nonnull'입니다.
ContextualAds contextualAds = new ContextualAds.Builder()
.setBuyer(new AdTechIdentifier.Builder().setIdentifier("https://example.com").build())
.setDecisionLogicUri(URI.create("example.com/decisionLogic"))
.setAdsWithBid(
List.of(
new AdWithBid.Builder()
.setAdData(new AdData.Builder()
.setMetadata("metadata")
.setAdRenderId("987")
.setRenderUri(URI.create("example.com/render"))
.build())
.setBid(5.10)
.build()))
.build();
아래 직렬화된 문자열에서 볼 수 있듯이 null을 허용하는 필드가 null로 설정되거나 전혀 설정되지 않은 경우 직렬화에서 생략됩니다. 하지만 NonNull 필드가 설정되어 있지 않으면 이 필드는 빈 문자열을 값으로 사용하여 직렬화에 포함됩니다.
"buyer=https://example.com|decision_logic_uri=example.com/decisionLogic|ads_with_bid=ad_data=ad_counter_keys=|ad_render_id=987|metadata=metadata|render_uri=example.com/render||bid=5.10||"
네트워크 호출 없이 문맥 광고 필터링
기기에 리마케팅 수요가 없으면 네트워크 호출 없이 문맥 광고의 광고 선택을 실행할 수 있습니다. 사전 빌드된 URI와 입찰가가 있는 문맥 광고 목록을 사용하면 플랫폼은 입찰 로직, 입찰 신호, 점수 신호 검색을 건너뛸 수 있습니다. 플랫폼은 사전 빌드된 URI를 사용하여 입찰가가 가장 높은 문맥 광고를 선택합니다.
지연 시간을 개선하기 위해 광고 기술은 네트워크 호출 없이 광고 필터링 기능이 있는 문맥 광고만 포함하는 광고 선택 흐름을 실행할 수 있습니다. 이는 점수 신호를 위해 사전 빌드된 URI를 사용하면 됩니다. scoreAds
구현 목록은 지원되는 사전 빌드된 URI 사용 사례 및 이름 섹션을 참고하세요.
네트워크 호출 없이 광고 선택을 실행하려면 다음 단계를 따르세요.
- 광고 필터링을 설정합니다.
- 문맥 광고를 만듭니다.
다음을 사용하여
AdSelectionConfig
객체를 만듭니다.- 빈 구매자 목록
- 최고 입찰가를 선택하는 사전 빌드된 URI
- 문맥 광고
- 점수 신호의 빈 URI. 빈 URI는 신뢰할 수 있는 점수 신호 가져오기를 사용하지 않으려 함을 나타낼 수 있습니다.
Uri prebuiltURIScoringUri = Uri.parse("ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=your.registered.uri/reporting"); // Initialize AdSelectionConfig AdSelectionConfig adSelectionConfig = new AdSelectionConfig.Builder() .setSeller(seller) .setDecisionLogicUri(prebuiltURIScoringUri) .setCustomAudienceBuyers(Collections.emptyList()) .setAdSelectionSignals(adSelectionSignals) .setSellerSignals(sellerSignals) .setPerBuyerSignals(perBuyerSignals) .setBuyerContextualAds(buyerContextualAds) .setTrustedScoringSignalsUri(Uri.EMPTY) .build();
광고 선택 실행
adSelectionManager.selectAds( adSelectionConfig, executor, outcomeReceiver);
사전 빌드된 URI를 사용하는 동안 자체 보고 JavaScript 실행
현재 개인 정보 보호 샌드박스 플랫폼에는 사전 빌드된 URI에 사용할 수 있는 기본 보고 JavaScript 구현만 있습니다. 지연 시간이 짧은 광고 선택을 위해 사전 빌드된 URI를 계속 사용하면서 자체 보고 JavaScript를 실행하려면 광고 선택과 보고 실행 간에 DecisionLogicUri
를 재정의하면 됩니다.
- 사전 빌드된 URI를 사용하여 문맥 광고의 광고 선택 실행 단계를 실행합니다.
보고를 실행하기 전에
AdSelectionConfig
사본을 만듭니다.adSelectionConfigWithYourReportingJS = adSelectionConfig.cloneToBuilder() // Replace <urlToFetchYourReportingJS> with your own URL: .setDecisionLogicUri(Uri.parse(<urlToFetchYourReportingJS>)) .build();
노출 보고 실행
// adSelectionId is from the result of the previous selectAds run ReportImpressionRequest request = new ReportImpressionRequest( adSelectionId, adSelectionConfigWithYourReportingJS); adSelectionManager.reportImpression( request, executor, outcomeReceiver);
폭포식 구조 미디에이션 실행
폭포식 구조 미디에이션을 사용하려면 여러 서드 파티 SDK(서드 파티 네트워크)를 퍼스트 파티 SDK 미디에이션 네트워크에서 조정해야 합니다. 폭포식 구조 미디에이션은 입찰이 기기에서 이뤄졌는지 또는 입찰 서비스(B&A)에서 실행되었는지와 관계없이 동일한 방식으로 실행됩니다.
서드 파티 네트워크
서드 파티 네트워크는 미디에이션 네트워크가 입찰을 실행하는 데 필요한 메서드를 호출할 수 있도록 하는 어댑터를 제공해야 합니다.
- 광고 선택 실행
- 노출 보고
다음은 미디에이션 네트워크 어댑터의 예입니다.
Kotlin
class NetworkAdaptor {
private val adSelectionManager : AdSelectionManager
init {
adSelectionManager = context.getSystemService(AdSelectionManager::class.java)
}
fun selectAds() {...}
fun reportImpressions() {...}
}
Java
class NetworkAdaptor {
AdSelectionManager adSelectionManager;
public NetworkAdaptor() {
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
}
public void selectAds() {...}
public void reportImpressions() {...}
}
각 SDK에는 자체 광고 선택 서비스 관리자 및 클라이언트와 자체 selectAds
및 reportImpressions
구현이 있습니다. SDK 제공업체는 기기 내 입찰을 위해 광고 선택을 실행하는 방법에 관한 섹션, 또는 B&A 입찰의 경우 B&A 설명 섹션을 참고할 수 있습니다. 광고 노출 보고 방법(보고용 단일 SSP 노출 보고 다음)을 따르세요.
미디에이션 네트워크
서드 파티 네트워크와 마찬가지로 미디에이션 네트워크에도 selectAds
및
reportImpression
구현이 필요합니다. 자세한 내용은 광고 선택 실행 방법 및 광고 노출 보고 방법에 관한 섹션을 참고하세요.
미디에이션 네트워크는 미디에이션 체인을 실행하고 미디에이션 체인에 자체를 배치하는 일을 담당합니다. 다음 섹션에서는 이 프로세스를 설정하고 실행하는 방법을 설명합니다.
미디에이션 체인 및 입찰가 하한선 가져오기
미디에이션 네트워크는 퍼스트 파티 문맥 광고, 미디에이션 체인, 서드 파티 네트워크의 입찰가 하한선을 검색합니다. 이는 미디에이션 네트워크에서
실행한 문맥 광고를 검색하는 요청에서 발생할 수 있습니다. 미디에이션 체인은 서드 파티 네트워크를 통해 반복하는 방법을 결정하고
입찰가 하한선은 adSelectionSignals
로 입찰 프로세스에 전달할 수 있습니다.
미디에이션 체인의 네트워크 배치
미디에이션 SDK는 실시간 퍼스트 파티 광고 입찰가의 eCPM을 기반으로 미디에이션 체인에 자체적으로 배치할 수 있습니다. Protected Audience API에서는 광고 입찰가가 불투명합니다. 미디에이션 SDK에서는 AdSelectionFromOutcomesConfig
를 사용해야 지정된 퍼스트 파티 광고의 입찰가를 체인에 있는 다음 서드 파티 네트워크의 입찰가 하한선과 비교할 수 있습니다. 퍼스트 파티 입찰가가 입찰가 하한선보다 높은 경우 미디에이션 SDK는 해당 서드 파티 네트워크 앞에 배치됩니다.
광고 선택 실행
퍼스트 파티 광고 조합을 가져오기 위해 미디에이션 네트워크는 광고 선택 실행 섹션의 단계에 따라 기기 내 입찰을 실행할 수 있습니다. 이렇게 하면
미디에이션 프로세스에 사용되는 AdSelectionId
, 퍼스트 파티 광고 조합, 입찰가가
생성됩니다.
AdSelectionFromOutcomesConfig 만들기
AdSelectionFromOutcomesConfig
를 사용하면 미디에이션 네트워크가 AdSelectionIds
목록(이전 입찰의 결과), 광고 선택 신호, URI을 전달하여 여러 조합에서 광고를 선택하는 JavaScript를 가져올 수 있습니다. 입찰가 및 신호와 함께 AdSelectionId의 목록이 JavaScript에 전달되어, 입찰가 하한선을 초과하는 경우 AdSelectionIds
의 하나를 반환하고 미디에이션 체인이 계속되어야 하는 경우 아무것도 반환하지 않을 수 있습니다.
미디에이션 네트워크는 이전 섹션의 퍼스트 파티 AdSelectionId
및 고려 중인 서드 파티 네트워크의 입찰가 하한선을 사용하여 AdSelectionFromOutcomesConfig
를 만듭니다. 미디에이션 체인의 각 단계에서 새 AdSelectionFromOutcomesConfig
를 만들어야 합니다.
Kotlin
fun runSelectOutcome(
adSelectionClient : AdSelectionClient,
outcome1p : AdSelectionOutcome,
network3p : NetworkAdapter) : ListenableFuture<AdSelectionOutcome?> {
val config = AdSelectionFromOutcomesConfig.Builder()
.setSeller(seller)
.setAdSelectionIds(listOf(outcome1p))
.setSelectionSignals({"bid_floor": bid_floor})
.setSelectionLogicUri(selectionLogicUri)
.build()
return adSelectionClient.selectAds(config)
}
Java
public ListenableFuture<AdSelectionOutcome> runSelectOutcome(AdSelectionOutcome outcome1p,
NetworkAdapter network3p) {
AdSelectionFromOutcomesConfig config = new AdSelectionFromOutcomesConfig.Builder()
.setSeller(seller)
.setAdSelectionIds(Collection.singletonList(outcome1p))
.setSelectionSignals({"bid_floor": bid_floor})
.setSelectionLogicUri(selectionLogicUri)
.build();
return adSelectionClient.selectAds(config){}
}
폭포식 구조 미디에이션의 selectAds()
메서드 재정의에는 AdSelectionFromOutcomesConfig
입력이 필요하며 여기서 다음과 같은 필수 매개변수를 지정해야 합니다.
- 판매자: 광고 선택을 시작하는 판매자 광고 네트워크의 식별자입니다.
- AdSelectionIds: 퍼스트 파티 광고에 관해 실행된 이전
selectAds()
의 싱글톤 목록입니다. - 광고 선택 신호: 구매자 입찰 로직으로 사용될 신호가 포함된 JSON 객체(문자열로 직렬화됨)입니다. 이 경우 지정된 서드 파티 네트워크에서 가져온 입찰가 하한선을 포함하세요.
- 선택 로직 URI: 광고 선택 중에 쿼리되는 HTTPS URL로, 낙찰된 광고를 선택하기 위해 미디에이션 네트워크의 JavaScript를 가져옵니다. 이 JavaScript에서 필수 함수 서명을 참고하세요. JavaScript는 입찰가가 입찰가 하한선보다 높은 경우 서드 파티 광고를 반환하고, 높지 않으면
null
을 반환해야 합니다. 이를 통해 미디에이션 SDK는 낙찰자가 발견될 때 미디에이션 체인을 자를 수 있습니다.
AdSelectionOutcomesConfig
가 만들어지면 체인에서 첫 번째로 있는 서드 파티 네트워크의 selectAds()
메서드를 호출하세요.
Kotlin
val adSelectionManager = context.getSystemService(AdSelectionManager::class.java)
// Initialize AdSelectionFromOutcomesConfig
AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig =
AdSelectionFromOutcomesConfig.Builder()
.setSeller(seller)
.setAdSelectionIds(listof(outcome1p))
.setSelectionSignals({"bid_floor": bid_floor})
.setSelectionLogicUri(selectionLogicUri)
.setAdSelectionIds(outcomeIds)
.build()
// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
adSelectionFromOutcomesConfig,
executor,
outcomeReceiver)
Java
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
// Initialize AdSelectionFromOutcomesConfig
AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig =
new AdSelectionFromOutcomesConfig.Builder()
.setSeller(seller)
.setAdSelectionIds(Collection.singletonList(outcome1p))
.setSelectionSignals({"bid_floor": bid_floor})
.setSelectionLogicUri(selectionLogicUri)
.setAdSelectionIds(outcomeIds)
.build();
// Run ad selection with AdSelectionConfig
adSelectionManager.selectAds(
adSelectionFromOutcomesConfig,
executor,
outcomeReceiver);
폭포식 구조 미디에이션 조정
다음은 미디에이션 프로세스를 통해 실행하기 위한 작업 순서입니다.
- 퍼스트 파티 광고 선택을 실행합니다.
- 미디에이션 체인을 반복합니다. 서드 파티 네트워크마다 다음을 실행합니다.
- 퍼스트 파티
outcomeId
및 서드 파티 SDK의 입찰가 하한선을 포함하는AdSelectionFromOutcomeConfig
를 빌드합니다. - 이전 단계의 구성으로
selectAds()
를 호출합니다. - 결과가 비어 있지 않으면 광고를 반환합니다.
- 현재 SDK 네트워크 어댑터의
selectAds()
메서드를 호출합니다. 결과가 비어 있지 않으면 광고를 반환합니다.
- 퍼스트 파티
- 체인에서 낙찰자가 없으면 퍼스트 파티 광고를 반환합니다.
Kotlin
fun runWaterfallMediation(mediationChain : List<NetworkAdapter>)
: Pair<AdSelectionOutcome, NetworkAdapter> {
val outcome1p = runAdSelection()
var outcome : AdSelectionOutcome
for(network3p in mediationChain) {
outcome = runSelectOutcome(outcome1p, network3p)
if (outcome1p.hasOutcome() && outcome.hasOutcome()) {
return Pair(outcome, this)
}
outcome = network3p.runAdSelection()
if(outcome.hasOutcome()) {
return Pair(outcome, network3p)
}
}
return Pair(outcome1p, this)
}
Java
class MediationNetwork {
AdSelectionManager adSelectionManager;
public MediationNetwork() {
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
}
public void runAdSelection() {...}
public void reportImpressions() {...}
public Pair<AdSelectionOutcome, NetworkAdapter> runWaterfallMediation(
List<NetworkAdapter> mediationChain) {
AdSelectionOutcome outcome1p = runAdSelection();
AdSelectionOutcome outcome;
for(NetworkAdapter network3p: mediationChain) {
if (outcome1p.hasOutcome() &&
(outcome = runSelectOutcome(outcome1p, network3p)).hasOutcome()) {
return new Pair<>(outcome, this);
}
if((outcome = network3p.runAdSelection()).hasOutcome()) {
return new Pair<>(outcome, network3p);
}
}
return new Pair<>(outcome1p, this);
}
/* Runs comparison by creating an AdSelectionFromOutcomesConfig */
public AdSelectionOutcome runSelectOutcome(AdSelectionOutcome outcome1p,
NetworkAdapter network3p) { ... }
}
광고 노출 보고
입찰 실행 방식에 따라 광고 노출을 보고하는 흐름은 두 가지입니다. 입찰을 실행하는 단일 SSP인 경우 다음 섹션을 따르세요. 폭포식 구조 미디에이션을 구현하려는 경우 폭포식 구조 미디에이션 노출 보고 섹션에 나와 있는 단계를 따르세요.
단일 SSP 노출 보고
광고 선택 워크플로에서 낙찰된 광고를 선택한 후에는 AdSelectionManager.reportImpression()
메서드를 사용하여, 참여 중인 구매 측 플랫폼과 판매 측 플랫폼에 노출을 다시 보고할 수 있습니다. 광고 노출을 보고하는 방법은 다음과 같습니다.
AdSelectionManager
객체를 초기화합니다.- 광고 선택 ID가 있는
ReportImpressionRequest
객체를 빌드합니다. ReportImpressionRequest
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기reportImpression()
메서드를 호출합니다.
Java
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
// Initialize a ReportImpressionRequest
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest.Builder()
.setAdSelectionId(adSelectionId)
.setAdSelectionConfig(adSelectionConfig)
.build();
// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportImpression(
reportImpressionRequest,
executor,
outcomeReceiver);
Kotlin
val adSelectionManager = context.getSystemService(AdSelectionManager::class.java)
// Initialize a ReportImpressionRequest
val adSelectionConfig: ReportImpressionRequest =
ReportImpressionRequest.Builder()
.setAdSelectionId(adSelectionId)
.setAdSelectionConfig(adSelectionConfig)
.build()
// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportImpression(
reportImpressionRequest,
executor,
outcomeReceiver)
다음 필수 매개변수를 사용하여 ReportImpressionRequest
를 초기화합니다.
- 광고 선택 ID: 성공한 광고 선택을 식별하는 기기 사용자 전용의 고유 ID입니다.
- 광고 선택 구성: 제공된 광고 선택 ID로 식별되는
selectAds()
호출에 사용된 것과 동일한 구성입니다.
비동기 reportImpression()
메서드는 OutcomeReceiver
객체를 사용하여 API 호출의 결과를 알립니다.
onResult()
콜백은 노출 보고 URL이 생성되고 요청이 예약되었는지를 나타냅니다.onError()
콜백은 다음과 같은 가능한 조건을 나타냅니다.- 잘못된 입력 인수로 호출이 초기화되면
AdServicesException
은IllegalArgumentException
을 원인으로 나타냅니다. - 그 외 모든 오류에서는
IllegalStateException
이 원인으로 표시된AdServicesException
을 수신하게 됩니다.
- 잘못된 입력 인수로 호출이 초기화되면
폭포식 구조 미디에이션 노출 보고
미디에이션 SDK는 낙찰된 SDK를 추적하여 보고 흐름을 트리거해야 합니다. 미디에이션 체인에 참여하는 SDK는 중재자가 자체 보고 흐름을 트리거하기 위해 호출할 수 있는 메서드를 제공해야 합니다. 조정된 입찰에 참여하는 SDK는 위 단계에 따라 자체 보고를 구현할 수 있습니다.
SSP는 다음 서드 파티 SDK 코드 예를 미디에이션 흐름에 참여하는 방법에 관한 프로토타입으로 사용할 수 있습니다.
Pair<AdSelectionOutcome, NetworkAdapter> winnerOutcomeAndNetwork =
mediationSdk.orchestrateMediation(mediationChain);
if (winner.first.hasOutcome()) {
winner.second.reportImpressions(winner.first.getAdSelectionId());
노출 보고 엔드포인트
Report impression API가 판매 측 플랫폼과 낙찰된 구매 측 플랫폼에서 제공한 엔드포인트에 HTTPS GET 요청을 발행합니다.
구매 측 플랫폼 엔드포인트:
- API는 맞춤 잠재고객에 지정된 입찰 로직 URL을 사용하여 노출 보고 URL을 반환하는 로직이 포함된 구매자 제공 JavaScript를 가져옵니다.
- 구매자의 노출 보고 URL을 반환해야 하는
reportWin()
JavaScript 함수를 호출합니다.
판매측 플랫폼 엔드포인트:
AdSelectionConfig
객체에 지정된 결정 로직 URL을 사용하여 판매자의 결정 로직 JavaScript를 가져옵니다.- 구매자의 노출 보고 URL을 반환해야 하는
reportResult()
JavaScript 함수를 호출합니다.
입찰 서비스 보고
입찰 서비스에서 실행되는 입찰에는 광고 상호작용 보고를 위해 생성된 URL을 포함하여 필요한 모든 보고 정보가 서버 측 입찰의 암호화된 응답에 포함됩니다. 응답이 복호화되면 적절한 URL이 플랫폼에 등록되므로 광고 및 노출 보고는 위에 나열된 것과 동일한 단계를 따릅니다.
최적의 노출 보고
reportImpression()
메서드는 최적으로 보고를 완료하도록 설계되었습니다.
광고 상호작용 보고
Protected Audience는 렌더링된 광고의 보다 세부적인 상호작용에 관한 보고를 지원합니다. 여기에는 조회 시간, 클릭수, 마우스 오버 또는 수집할 수 있는 기타 유용한 측정항목과 같은 상호작용이 포함될 수 있습니다. 이러한 보고서를 수신하려면 두 단계를 거쳐야 합니다. 먼저 구매자와 판매자가 보고 JavaScript에서 이러한 보고서를 수신하려면 등록해야 합니다. 그런 다음, 클라이언트가 이러한 이벤트를 보고해야 합니다.
상호작용 이벤트 수신 등록
상호작용 이벤트 등록은 플랫폼에서 제공하는 JavaScript 함수 registerAdBeacon
을 사용하여 구매자의 reportWin()
및 판매자의 reportResult()
JavaScript 함수에서 이루어집니다. 이벤트 보고서를 수신하기 위해 등록하려면 보고 JavaScript에서 플랫폼 JavaScript 함수를 호출하면 됩니다. 다음 스니펫은 구매자의 reportWin()
을 사용하지만 reportResult()
에도 동일한 접근 방식이 적용됩니다.
reportWin(
adSelectionSignals,
perBuyerSignals,
signalsForBuyer,
contextualSignals,
customAudienceSignals) {
...
// Calculate reportingUri, clickUri, viewUri, and hoverUri
registerAdBeacon("click", clickUri)
registerAdBeacon("view", viewUri)
registerAdBeacon("hover", hoverUri)
return reportingUrl;
}
상호작용 이벤트 보고
노출을 보고한 후 클라이언트는 AdSelectionManager.reportInteraction()
메서드를 사용하여 이전에 등록된 낙찰 구매 측 및 판매 측 플랫폼에 상호작용을 다시 보고할 수 있습니다. 광고 이벤트를 보고하려면 다음 단계를 따르세요.
AdSelectionManager
객체를 초기화합니다.- 광고 선택 ID, 상호작용 키, 상호작용 데이터, 보고 대상을 사용하여
ReportInteractionRequest
객체를 빌드합니다. request
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기reportInteraction()
메서드를 호출합니다.
AdSelectionManager adSelectionManager =
context.getSystemService(AdSelectionManager.class);
// Initialize a ReportInteractionRequest
ReportInteractionRequest request =
new ReportInteractionRequest.Builder()
.setAdSelectionId(adSelectionId)
.setInteractionKey("view")
.setInteractionData("{ viewTimeInSeconds : 1 }") // Can be any string
.setReportingDestinations(
FLAG_REPORTING_DESTINATION_BUYER | FLAG_REPORTING_DESTINATION_SELLER
)
.build();
// Request to report the impression with the ReportImpressionRequest
adSelectionManager.reportInteraction(
reportImpressionRequest,
executor,
outcomeReceiver);
다음 필수 매개변수를 사용하여 ReportInteractionRequest
를 초기화합니다.
- 광고 선택 ID: 이전에 반환된
AdSelectionOutcome
에서 가져온 광고 선택 ID입니다. - 상호작용 키: 보고되는 작업을 설명하는 클라이언트가 정의한 문자열 키입니다. 이는 보고 JavaScript 함수에서 판매자 또는 구매자가 등록한 키와 일치해야 합니다.
- 상호작용 데이터: 이벤트 보고에 포함되고 보고 서버에 다시 POST될 데이터가 포함된 문자열입니다.
- 보고 대상: 이벤트를 구매자, 판매자 또는 둘 다에 보고해야 하는지 지정하는 비트 마스크입니다. 이러한 플래그는 플랫폼에서 제공하며 최종 대상 마스크는 비트 연산을 사용하여 만들 수 있습니다. 하나의 대상에 보고하려면 플랫폼에서 직접 제공하는 플래그를 사용하면 됩니다. 여러 대상에 보고하려면 비트 OR(
|
)을 사용하여 플래그 값을 결합할 수 있습니다.
비동기 reportInteraction()
메서드는 OutcomeReceiver
객체를 사용하여 API 호출의 결과를 알립니다.
onResult()
콜백은 보고서 상호작용 호출이 유효함을 나타냅니다.onError()
콜백은 다음과 같은 가능한 조건을 나타냅니다.- 앱이 백그라운드에서 실행 중일 때 호출이 이루어지면 실패에 관한 설명이 있는
IllegalStateException
이 반환됩니다. - 클라이언트가
reportInteraction()
호출에서 제한되면LimitExceededException
이 반환됩니다. - 패키지가 개인 정보 보호 API를 호출하도록 등록되지 않은 경우
SecurityException()
이 반환됩니다. - 상호작용을 보고하는 앱이
selectAds()
를 호출한 앱과 다르면IllegalStateException
이 반환됩니다.
- 앱이 백그라운드에서 실행 중일 때 호출이 이루어지면 실패에 관한 설명이 있는
- 사용자가 개인 정보 보호 샌드박스 API를 사용 설정하는 데 동의하지 않은 경우 호출이 자동으로 실패합니다.
상호작용 보고 엔드포인트
보고서 상호작용 API는 판매 측 플랫폼 및 낙찰된 구매 측 플랫폼에서 제공한 엔드포인트에 HTTPS POST 요청을 발행합니다. Protected Audience는 상호작용 키를 보고 JavaScript에서 선언된 URI와 일치시키고 보고되는 각 상호작용의 각 엔드포인트에 POST 요청을 발행합니다. 요청의 콘텐츠 유형은 일반 텍스트이고 상호작용 데이터인 본문이 포함되어 있습니다.
최선의 상호작용 보고
reportInteraction()
은 HTTP POST를 통해 보고를 최상의 방식으로 완료할 수 있도록 설계되었습니다.
일일 백그라운드 업데이트
맞춤 잠재고객을 만들 때 앱 또는 SDK에서는 맞춤 잠재고객 메타데이터를 초기화할 수 있습니다. 또한 플랫폼에서는 일일 백그라운드 업데이트 프로세스로 다음과 같은 맞춤 잠재고객 메타데이터를 업데이트할 수 있습니다.
- 사용자 입찰 신호
- 신뢰할 수 있는 입찰 데이터
AdData
목록
이 프로세스는 맞춤 잠재고객에 정의된 일일 업데이트 URL을 대상으로 쿼리하며 이 URL은 JSON 응답을 반환할 수 있습니다.
- JSON 응답에는 업데이트해야 하는, 지원되는 메타데이터 필드가 포함될 수 있습니다.
- 각 JSON 필드는 독립적으로 검증됩니다. 클라이언트는 형식이 잘못된 필드를 무시하므로 응답의 해당 필드가 업데이트되지 않습니다.
- 빈 HTTP 응답이나 빈 JSON 객체 '
{}
'로 인해 메타데이터가 업데이트되지 않습니다. - 응답 메시지 크기는 10KB로 제한해야 합니다.
- 모든 URI는 HTTPS를 사용해야 합니다.
trusted_bidding_uri
는 구매자와 동일한 ETLD+1을 공유해야 합니다.
예: 백그라운드 일일 업데이트 JSON 응답
{
"user_bidding_signals" : { ... }, // Valid JSON object
"trusted_bidding_data" : {
"trusted_bidding_uri" : 'example-dsp1-key-value-service.com',
"trusted_bidding_keys" : [ 'campaign123', 'campaign456', ... ]
},
'ads' : [
{
"render_uri" : 'www.example-dsp1.com/.../campaign123.html',
'metadata' : { ... } // Valid JSON object
},
{
"render_uri" : 'www.example-dsp1.com/.../campaign456.html',
'metadata' : { ... } // Valid JSON object
},
...
]
}
광고 선택을 위한 JavaScript
광고 선택 워크플로는 구매자 제공 JavaScript와 판매자 제공 JavaScript의 실행을 조정합니다.
구매자 제공 JavaScript는 맞춤 잠재고객에 지정된 입찰 로직 URL에서 가져옵니다. 반환된 JavaScript에는 다음 함수가 포함되어야 합니다.
판매자 제공 JavaScript는 광고 선택 API의 AdSelectionConfig
매개변수에 지정된 결정 로직 URL에서 가져오게 됩니다. 반환된 JavaScript에는 다음 함수가 포함되어야 합니다.
generateBid()
function generateBid(
ad,
auction_signals,
per_buyer_signals,
trusted_bidding_signals,
contextual_signals,
user_signals,
custom_audience_bidding_signals) {
return {'status': 0, 'ad': ad, 'bid': ad.metadata.result };
}
입력 매개변수:
ad
:var ad = { 'render_url': url, 'metadata': json_metadata };
형식의 JSON 객체입니다.auction_signals, per_buyer_signals
: JSON 객체로, 입찰 구성 객체에 지정됩니다.custom_audience_bidding_signals
: 플랫폼에서 생성된 JSON 객체입니다. 이 JSON 객체의 형식은 다음과 같습니다.var custom_audience_signals = { "owner":"ca_owner", "buyer":"ca_buyer", "name":"ca_name", "activation_time":"ca_activation_time_epoch_ms", "expiration_time":"ca_expiration_time_epoch_ms", "user_bidding_signals":"ca_user_bidding_signals" }
여기에서:
owner
,buyer
,name
은 광고 선택에 참여 중인 맞춤 잠재고객과 동일한 이름을 가진 속성에서 가져온 문자열입니다.activation_time
및expiration_time
은 맞춤 잠재고객의 활성화 시간 및 만료 시간으로, Unix 에포크 이후 초 단위로 표현됩니다.ca_user_bidding_signals
는 생성 시CustomAudience
의userBiddingSignals
필드에 지정된 JSON 문자열입니다.trusted_bidding_signals, contextual_signals
및user_signals
는 JSON 객체입니다. 이러한 객체는 빈 객체로 전달되며 향후 출시에서 채워질 예정입니다. 객체의 형식은 플랫폼에 의해 적용되지 않으며 광고 기술을 통해 관리됩니다.
결과:
ad
: 입찰에 참조되는 광고입니다. 스크립트는 다양한 메타데이터와 함께 수신한 광고의 사본을 반환할 수 있습니다. 광고의render_url
속성은 변경되지 않아야 합니다.bid
: 이 광고의 입찰가를 나타내는 부동 소수점 값status
: 정수로, 다음에 해당할 수 있습니다.0
: 실행이 성공한 경우1
: (또는 0이 아닌 값) 입력 신호가 잘못된 모든 경우. 입찰 생성에 의해 0이 아닌 값이 반환되는 경우 모든 CA 광고의 입찰 프로세스가 무효화됩니다.
scoreAd()
function scoreAd(
ad,
bid,
ad_selection_config,
seller_signals,
trusted_scoring_signals,
contextual_signal,
user_signal,
custom_audience_signal) {
return {'status': 0, 'score': score };
}
입력 매개변수:
ad
:generateBid
문서를 참고하세요.bid
: 광고의 입찰가ad_selection_config
:selectAds
API의AdSelectionConfig
매개변수를 나타내는 JSON 객체. 형식은 다음과 같습니다.var ad_selection_config = { 'seller': 'seller', 'decision_logic_url': 'url_of_decision_logic', 'custom_audience_buyers': ['buyer1', 'buyer2'], 'auction_signals': auction_signals, 'per_buyer_signals': per_buyer_signals, 'contextual_ads': [ad1, ad2] }
seller_signals
:sellerSignals
AdSelectionConfig
API 매개변수에서 읽어온 JSON 객체trusted_scoring_signal
:AdSelectionConfig
API 매개변수의adSelectionSignals
필드에서 읽어옴contextual_signals, user_signals
: JSON 개체 현재 이러한 객체는 빈 객체로 전달되며 향후 출시 버전에서 채워질 예정입니다. 객체의 형식은 플랫폼에 의해 적용되지 않으며 광고 기술을 통해 관리됩니다.per_buyer_signals
: 현재 맞춤 잠재고객 구매자를 키로 사용하여AdSelectionConfig
API 매개변수의perBuyerSignal
맵에서 읽어온 JSON 객체. 지정된 구매자에 관한 항목이 맵에 없으면 비어 있습니다.
출력:
score
: 이 광고의 평가 결과를 나타내는 부동 소수점 값status
: 정수로, 다음에 해당할 수 있습니다.- 0: 실행이 성공한 경우
- 1:
customAudienceSignals
가 유효하지 않은 경우 - 2:
AdSelectionConfig
가 유효하지 않은 경우 - 3: 다른 신호가 유효하지 않은 경우
- 0이 아닌 값으로 인해 프로세스 실패가 발생하며, 발생한 예외 유형이 값에 따라 결정됩니다.
selectOutcome()
function selectOutcome(
outcomes,
selection_signals) {
return {'status': 0, 'result': null};
}
입력 매개변수:
outcomes
: JSON 객체{"id": id_string, "bid": bid_double}
입니다.selection_signals
: JSON 객체로, 입찰 구성 객체에 지정됩니다.
출력:
status
: 성공한 경우에는0
이고, 실패한 경우에는 0이 아닌 값입니다.result
: 전달된 결과 중 하나 또는 null입니다.
reportResult()
function reportResult(ad_selection_config, render_url, bid, contextual_signals) {
return {
'status': status,
'results': {'signals_for_buyer': signals_for_buyer, 'reporting_url': reporting_url }
};
}
입력 매개변수:
ad_selection_config
:scoreAds
의 문서를 참고하세요.render_url
: 낙찰된 광고의 렌더링 URLbid
: 낙찰된 광고에 제공된 입찰contextual_signals
:generateBid
의 문서를 참고하세요.
출력:
- 성공한 경우에는
status: 0
이고, 실패한 경우에는 0이 아닌 값입니다. results
: 다음을 포함하는 JSON 객체:signals_for_buyer
:reportWin
함수에 전달되는 JSON 객체입니다.reporting_url
: 플랫폼에서 구매자에게 노출을 알리는 데 사용하는 URL입니다.
reportWin()
function reportWin(
ad_selection_signals,
per_buyer_signals,
signals_for_buyer,
contextual_signals,
custom_audience_signals) {
return {'status': 0, 'results': {'reporting_url': reporting_url } };
}
입력 매개변수:
ad_selection_signals, per_buyer_signals
:scoreAd
관련 문서 참고signals_for_buyer
:reportResult
에서 반환한 JSON 객체contextual_signals, custom_audience_signals
:generateBid
관련 문서 참고
출력:
- 성공한 경우에는
status: 0
이고, 실패한 경우에는 0이 아닌 값입니다. results
: 다음을 포함하는 JSON 객체입니다.reporting_url
: 플랫폼에서 판매자에게 노출을 알리는 데 사용하는 URL입니다.
registerAdBeacon()
function registerAdBeacon(
interaction_key,
reporting_uri
)
입력 매개변수:
interaction_key
: 이벤트를 나타내는 문자열입니다. 이는 알림을 받아야 하는reporting_uri
를 조회하기 위해 이벤트 상호작용을 보고할 때 나중에 플랫폼에서 사용됩니다. 이 키는 구매자 또는 판매자가 등록하는 내용과 판매자가 보고하는 내용 간에 일치해야 합니다.reporting_uri
: 이벤트 보고서를 수신하는 URI입니다. 이는 보고되는 이벤트 유형에 따라 다릅니다. 이벤트와 함께 보고된 데이터를 처리하려면 POST 요청을 수락해야 합니다.
사전 빌드된 광고 선택 URI
사전 빌드된 URI를 사용하면 광고 기술이 AdSelectionConfig
및 AdSelectionFromOutcomesConfig
클래스에서 광고 선택 결정 로직의 JavaScript 함수를 지정할 수 있습니다. 사전 빌드된 URI는 해당 JavaScript를 다운로드하기 위해 네트워크 호출이 필요하지 않습니다. 광고 기술은 등록된 도메인을 설정하여 JavaScript를 호스팅하지 않고도 사전 빌드된 URI를 사용할 수 있습니다.
사전 빌드된 URI는 다음 형식을 사용하여 생성됩니다.
ad-selection-prebuilt:<use-case>/<name>?<required-script-generation-parameters>
개인 정보 보호 샌드박스 플랫폼은 런타임 시 이 URI의 정보를 사용하여 JavaScript를 제공합니다.
다음과 같은 경우 IllegalArgumentException
이 발생합니다.
- 필수 매개변수가 URI에 없습니다.
- URI에 인식할 수 없는 매개변수가 있습니다.
지원되는 사전 빌드된 URI 사용 사례 및 이름
사용 사례 1: ad-selection
ad-selection
사용 사례 아래 사전 빌드된 URI는 selectAds(AdSelectionConfig)
흐름에서 지원됩니다.
사전 빌드된 URI 이름: highest-bid-wins
사전 빌드된 이 URI는 입찰 후 입찰가가 가장 높은 광고를 선택하는 JavaScript를 제공합니다. 또한 낙찰자의 render_uri
및 bid
를 보고하는
기본 보고 함수를 제공합니다.
필수 매개변수
reportingUrl
: render_uri
및 낙찰된 광고의 bid
로 매개변수화되는
기본 보고 URL입니다.
<reportingUrl>?render_uri=<renderUriOfWinnigAd>&bid=<bidOfWinningAd>
사용 정보
기본 보고 URL이 https://www.ssp.com/reporting
이면 사전 빌드된 URI는 다음과 같습니다.
`ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=https://www.ssp.com/reporting`
사용 사례 2: ad-selection-from-outcomes
ad-selection-from-outcomes
사용 사례 아래 사전 빌드된 URI는 selectAds(AdSelectionFromOutcomesConfig)
워크플로를 지원합니다.
사전 빌드된 URI 이름: waterfall-mediation-truncation
사전 빌드된 waterfall-mediation-truncation
URI는 폭포식 구조 미디에이션 자르기 로직을 구현하는
JavaScript를 제공하며 여기서 JavaScript는 bid
가 bid floor
보다 높거나 같은 경우 퍼스트 파티 광고를 반환하고 그 외 경우에는 null
을 반환합니다.
필수 매개변수
bidFloor
: getSelectionSignals()
에 전달된 입찰가 하한선 값의 키로, 미디에이션 SDK의 광고와 비교됩니다.
사용 정보
광고 선택 신호가 {"bid_floor": 10}
과 같이 표시되면 사전 빌드된 결과 URI는 다음과 같습니다.
`ad-selection-prebuilt://ad-selection-from-outcomes/waterfall-mediation-truncation/?bidFloor=bid_floor`
테스트
Protected Audience API를 시작하는 데 도움을 드리고자 Kotlin과 자바로 샘플 앱을 만들었으며 이 앱은 GitHub에서 확인할 수 있습니다.
사전 요건
Protected Audience API를 사용하려면 광고 선택 및 노출 보고 중에 JavaScript가 필요합니다. 테스트 환경에서 이 JavaScript를 제공하는 방법에는 두 가지가 있습니다.
- JavaScript를 반환하는 필수 HTTPS 엔드포인트로 서버를 실행합니다.
- 로컬 소스에서 필요한 코드를 제공하여 원격 가져오기를 재정의합니다.
두 방법 모두 HTTPS 엔드포인트를 설정하여 노출 보고를 처리해야 합니다.
HTTPS 엔드포인트
광고 선택 및 노출 보고를 테스트하려면 테스트 기기 또는 에뮬레이터가 액세스할 수 있는 다음 7개의 HTTPS 엔드포인트를 설정해야 합니다.
- 입찰 로직 JavaScript를 처리하는 구매자 엔드포인트
- 입찰 신호를 처리하는 엔드포인트
- 결정 로직 JavaScript를 처리하는 판매자 엔드포인트
- 점수 신호를 처리하는 엔드포인트
- 낙찰자 노출 보고 엔드포인트
- 판매자 노출 보고 엔드포인트
- 맞춤 잠재고객 일일 업데이트를 처리하는 엔드포인트
편의를 위해 GitHub 저장소에서는 테스트 목적으로 기본 JavaScript 코드를 제공합니다. 지원되는 모의 플랫폼 또는 마이크로서비스 플랫폼에 배포할 수 있는 OpenAPI 서비스 정의도 포함하고 있습니다. 자세한 내용은 프로젝트 리드미를 참고하세요.
JavaScript의 원격 가져오기 재정의
이 기능은 엔드 투 엔드 테스트에 사용하기 위한 것입니다. 원격 가져오기를 재정의하려면 개발자 옵션이 사용 설정된 상태에서 앱을 디버그 모드로 실행해야 합니다.
애플리케이션의 디버그 모드를 사용 설정하려면 AndroidManifest.xml의 애플리케이션 속성에 다음 줄을 추가하세요.
<application
android:debuggable="true">
이러한 재정의를 사용하는 방법을 보여주는 예는 GitHub의 Protected Audience API 샘플 앱을 참고하세요.
입찰, 점수 결정, 보고와 같은 광고 선택 루틴을 처리하려면 자체 맞춤 JavaScript를 추가해야 합니다. 필요한 모든 요청을 처리하는 기본적인 JavaScript 코드 예는 GitHub 저장소에서 확인할 수 있습니다. Protected Audience API 샘플 애플리케이션은 해당 파일에서 코드를 읽고 재정의로 사용할 수 있도록 준비하는 방법을 보여줍니다.
판매 측 및 구매 측 JavaScript 가져오기를 독립적으로 재정의할 수 있지만 개발자가 재정의를 제공하지 않는 JavaScript를 제공하려면 HTTPS 엔드포인트가 필요합니다. 이러한 사례를 처리하는 서버를 설정하는 방법은 리드미를 참고하세요.
개발자는 자신의 패키지에서 소유한 맞춤 잠재고객의 JavaScript 가져오기만 재정의할 수 있습니다.
판매 측 JavaScript 재정의
판매 측 JavaScript 재정의를 설정하려면 다음을 실행하세요(아래 코드 예 참고).
AdSelectionManager
객체를 초기화합니다.AdSelectionManager
객체에서TestAdSelectionManager
참조를 가져옵니다.AdSelectionConfig
객체를 빌드합니다.AdSelectionConfig
객체와 재정의로 사용할 JavaScript를 나타내는String
으로AddAdSelectionOverrideRequest
를 빌드합니다.AddAdSelectionOverrideRequest
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기overrideAdSelectionConfigRemoteInfo()
메서드를 호출합니다.
Kotlin
val testAdSelectionManager: TestAdSelectionManager =
context.getSystemService(AdSelectionManager::class.java).getTestAdSelectionManager()
// Initialize AdSelectionConfig =
val adSelectionConfig = new AdSelectionConfig.Builder()
.setSeller(seller)
.setDecisionLogicUrl(decisionLogicUrl)
.setCustomAudienceBuyers(customAudienceBuyers)
.setAdSelectionSignals(adSelectionSignals)
.setSellerSignals(sellerSignals)
.setPerBuyerSignals(perBuyerSignals)
.build()
// Initialize AddAddSelectionOverrideRequest
val request = AddAdSelectionOverrideRequest.Builder()
.setAdSelectionConfig(adSelectionConfig)
.setDecisionLogicJs(decisionLogicJS)
.build()
// Run the call to override the JavaScript for the given AdSelectionConfig
// Note that this only takes effect in apps marked as debuggable
testAdSelectionManager.overrideAdSelectionConfigRemoteInfo(
request,
executor,
outComeReceiver)
Java
TestAdSelectionManager testAdSelectionManager =
context.getSystemService(AdSelectionManager.class).getTestAdSelectionManager();
// Initialize AdSelectionConfig =
AdSelectionConfig adSelectionConfig = new AdSelectionConfig.Builder()
.setSeller(seller)
.setDecisionLogicUrl(decisionLogicUrl)
.setCustomAudienceBuyers(customAudienceBuyers)
.setAdSelectionSignals(adSelectionSignals)
.setSellerSignals(sellerSignals)
.setPerBuyerSignals(perBuyerSignals)
.build();
// Initialize AddAddSelectionOverrideRequest
AddAdSelectionOverrideRequest request = AddAdSelectionOverrideRequest.Builder()
.setAdSelectionConfig(adSelectionConfig)
.setDecisionLogicJs(decisionLogicJS)
.build();
// Run the call to override the JavaScript for the given AdSelectionConfig
// Note that this only takes effect in apps marked as debuggable
testAdSelectionManager.overrideAdSelectionConfigRemoteInfo(
request,
executor,
outComeReceiver);
AdSelectionConfig
의 각 필드가 나타내는 항목에 관한 자세한 내용은 광고 선택 실행 섹션을 참고하세요. 주요 차이점은 decisionLogicUrl이 무시되므로 이를 자리표시자 값으로 설정할 수 있다는 것입니다.
광고 선택 중에 사용되는 JavaScript를 재정의하려면 decisionLogicJs
에 적절한 판매자 측 함수 서명이 포함되어야 합니다.
JavaScript 파일을 문자열로 읽는 방법에 관한 예는 GitHub의 Protected Audience API 샘플 앱을 참고하세요.
비동기 overrideAdSelectionConfigRemoteInfo()
메서드는 OutcomeReceiver
객체를 사용하여 API 호출의 결과를 알립니다.
onResult()
콜백은 재정의가 적용되었음을 나타냅니다.
향후 selectAds()
호출에서는 개발자가 재정의로 전달한 결정 및 보고 로직을 사용합니다.
onError()
콜백은 두 가지 가능한 조건을 나타냅니다.
- 잘못된 인수로 재정의를 시도하면
AdServiceException
은IllegalArgumentException
을 원인으로 나타냅니다. - 개발자 옵션이 사용 설정된 상태에서 디버그 모드로 실행되지 않는 앱으로 재정의를 시도하면
AdServiceException
에서IllegalStateException
을 원인으로 표시합니다.
판매 측 재정의 재설정
이 섹션에서는 개발자가 판매 측 JavaScript를 재정의했으며 이전 섹션에서 사용된 TestAdSelectionManager
및 AdSelectionConfig
참조가 있다고 가정합니다.
모든 AdSelectionConfigs
의 재정의를 재설정하려면 다음 안내를 따르세요.
- 관련
OutcomeReceiver
객체를 사용하여 비동기resetAllAdSelectionConfigRemoteOverrides()
메서드를 호출합니다.
Kotlin
// Resets overrides for all AdSelectionConfigs
testAadSelectionManager.resetAllAdSelectionConfigRemoteOverrides(
outComeReceiver)
Java
// Resets overrides for all AdSelectionConfigs
testAdSelectionManager.resetAllAdSelectionConfigRemoteOverrides(
outComeReceiver);
판매 측 재정의를 재설정한 후 selectAds()
호출에서는 AdSelectionConfig
에 저장된 decisionLogicUrl을 사용하여 필수 JavaScript를 가져오려고 합니다.
resetAllAdSelectionConfigRemoteOverrides()
호출이 실패하면 OutComeReceiver.onError()
콜백에서 AdServiceException
을 제공합니다.
개발자 옵션이 사용 설정된 상태에서 디버그 모드로 실행되지 않는 앱으로 재정의 삭제를 시도하면 AdServiceException
에서 IllegalStateException
을 원인으로 표시합니다.
구매 측 JavaScript 재정의
- 단계에 따라 맞춤 잠재고객에 참여합니다.
- 재정의할 맞춤 잠재고객의 구매자, 이름과 재정의로 사용하려는 입찰 로직과 데이터를 사용하여
AddCustomAudienceOverrideRequest
를 빌드합니다. AddCustomAudienceOverrideRequest
객체와 관련Executor
및OutcomeReceiver
객체를 사용하여 비동기overrideCustomAudienceRemoteInfo()
메서드를 호출합니다.
Kotlin
val testCustomAudienceManager: TestCustomAudienceManager =
context.getSystemService(CustomAudienceManager::class.java).getTestCustomAudienceManager()
// Join custom audience
// Build the AddCustomAudienceOverrideRequest
val request = AddCustomAudienceOverrideRequest.Builder()
.setBuyer(buyer)
.setName(name)
.setBiddingLogicJs(biddingLogicJS)
.setTrustedBiddingSignals(trustedBiddingSignals)
.build()
// Run the call to override JavaScript for the given custom audience
testCustomAudienceManager.overrideCustomAudienceRemoteInfo(
request,
executor,
outComeReceiver)
Java
TestCustomAudienceManager testCustomAudienceManager =
context.getSystemService(CustomAudienceManager.class).getTestCustomAudienceManager();
// Join custom audience
// Build the AddCustomAudienceOverrideRequest
AddCustomAudienceOverrideRequest request =
AddCustomAudienceOverrideRequest.Builder()
.setBuyer(buyer)
.setName(name)
.setBiddingLogicJs(biddingLogicJS)
.setTrustedBiddingSignals(trustedBiddingSignals)
.build();
// Run the call to override JavaScript for the given custom audience
testCustomAudienceManager.overrideCustomAudienceRemoteInfo(
request,
executor,
outComeReceiver);
, 구매자, 이름의 값은 맞춤 잠재고객을 만드는 데 사용된 값과 동일합니다. 이 필드에 관해 자세히 알아보기
또한 추가 매개변수 두 개를 지정할 수 있습니다.
biddingLogicJs
: 광고 선택 중에 사용되는 구매자의 로직을 보유하는 JavaScript입니다. 이 JavaScript에서 필수 함수 서명을 참고하세요.trustedBiddingSignals
: 광고 선택 중에 사용할 입찰 신호입니다. 테스트 목적으로 이는 빈 문자열일 수 있습니다.
비동기 overrideCustomAudienceRemoteInfo()
메서드는 OutcomeReceiver
객체를 사용하여 API 호출의 결과를 알립니다.
onResult()
콜백은 재정의가 적용되었음을 나타냅니다.
후속 selectAds()
호출에서는 개발자가 재정의로 전달한 입찰 및 보고 로직을 사용합니다.
onError()
콜백은 두 가지 가능한 조건을 나타냅니다.
- 잘못된 인수로 재정의를 시도하면
AdServiceException
은IllegalArgumentException
을 원인으로 나타냅니다. - 개발자 옵션이 사용 설정된 상태에서 디버그 모드로 실행되지 않는 앱으로 재정의를 시도하면
AdServiceException
에서IllegalStateException
을 원인으로 표시합니다.
구매 측 재정의 재설정
이 섹션에서는 개발자가 구매 측 JavaScript를 재정의했으며 이전 섹션에서 사용된 TestCustomAudienceManager
참조가 있다고 가정합니다.
모든 맞춤 잠재고객의 재정의를 재설정하려면 다음 안내를 따르세요.
- 관련
Executor
및OutcomeReceiver
객체를 사용하여 비동기resetAllCustomAudienceOverrides()
메서드를 호출합니다.
Kotlin
// Resets overrides for all custom audiences
testCustomAudienceManager.resetCustomAudienceRemoteInfoOverride(
executor,
outComeReceiver)
Java
// Resets overrides for all custom audiences
testCustomAudienceManager.resetCustomAudienceRemoteInfoOverride(
executor,
outComeReceiver)
구매 측 재정의를 재설정한 후 후속 selectAds()
호출에서는 CustomAudience
에 저장된 biddingLogicUrl
및 trustedBiddingData
를 사용하여 필수 JavaScript를 가져오려고 합니다.
resetCustomAudienceRemoteInfoOverride()
호출이 실패하면 OutComeReceiver.onError()
콜백에서 AdServiceException
을 제공합니다.
개발자 옵션이 사용 설정된 상태에서 디버그 모드로 실행되지 않는 앱으로 재정의 삭제를 시도하면 AdServiceException
에서 IllegalStateException
을 원인으로 표시합니다.
보고 서버 설정
원격 가져오기 재정의를 사용하는 경우에도 기기 또는 에뮬레이터에서 도달할 수 있는 서버를 설정하여 보고 이벤트에 응답해야 합니다. 200을 반환하는 간단한 엔드포인트면 테스트하기에 충분합니다. GitHub 저장소에는 지원되는 모의 플랫폼 또는 마이크로서비스 플랫폼에 배포할 수 있는 OpenAPI 서비스 정의가 포함되어 있습니다. 자세한 내용은 프로젝트 리드미를 참고하세요.
OpenAPI 정의를 찾는 경우 reporting-server.json을 찾으세요.
이 파일에는 HTTP 응답 코드를 나타내는 200을 반환하는 간단한 엔드포인트가 포함되어 있습니다. 이 엔드포인트는 selectAds()
중에 사용되며 노출 보고가 성공적으로 완료되었음을 Protected Audience API에 알립니다.
테스트할 기능
- 이전 사용자 작업을 기반으로 맞춤 잠재고객을 설정하고 이에 가입 또는 탈퇴하는 과정 실행
- 원격으로 호스팅되는 JavaScript를 통해 기기 내 광고 선택을 시작하는 과정 실행
- 앱과 맞춤 잠재고객 설정의 연결이 광고 선택 결과에 미칠 수 있는 영향 관찰
- 광고 선택 후 노출 보고 실행
제한사항
다음 표에는 Protected Audience API 처리 제한사항이 나와 있습니다. 표시된 제한사항은 피드백에 따라 변경될 수 있습니다. 진행 중인 기능은 출시 노트를 참고하세요.
구성요소 | 제한 설명 | 제한 값 |
---|---|---|
맞춤 잠재고객(CA) | CA당 최대 광고 수 | 100 |
애플리케이션당 최대 CA 수 | 1000 | |
CA를 만들 수 있는 최대 앱 수 | 1000 | |
생성 시간부터 CA가 활성화될 때까지 걸리는 최대 지연 시간 | 60일 | |
활성화 시간부터 최대 CA 만료 시간 | 60일 | |
기기의 최대 CA 수 | 4000 | |
CA 이름의 최대 크기 | 200바이트 | |
일일 가져오기 URI의 최대 크기 | 400바이트 | |
입찰 로직 URI의 최대 크기 | 400바이트 | |
신뢰할 수 있는 입찰 데이터의 최대 크기 | 10KB | |
사용자 입찰 신호의 최대 크기 | 10KB | |
구매자당 최대 leaveCustomAudience 호출률 |
초당 1회 | |
구매자당 최대 joinCustomAudience 호출률 |
초당 1회 | |
CA 백그라운드 가져오기 | 연결 시간 제한 | 5초 |
HTTP 읽기 시간 제한 | 30초 | |
최대 총 다운로드 크기 | 10KB | |
최대 가져오기 반복 시간 | 5분 | |
작업당 업데이트되는 최대 CA 수 | 1000 | |
광고 선택 | 최대 구매자 수 | 미정 |
구매자당 최대 CA 수 | 미정 | |
입찰 내 최대 광고 수 | 미정 | |
초기 연결 시간 제한 | 5초 | |
연결 읽기 시간 제한 | 5초 | |
전체 AdSelection 의 최대 실행 시간 |
10초 | |
AdSelection 에서 CA당 최대 입찰 실행 시간 |
5초 | |
AdSelection 에서 최대 점수 실행 시간 |
5초 | |
AdSelection 에서 구매자당 최대 실행 시간 |
미정 | |
광고 선택/판매자/구매자당 신호의 최대 크기 | 미정 | |
판매자/구매자 스크립트의 최대 크기 | 미정 | |
selectAds 의 최대 호출률 |
1QPS | |
노출 보고 | 지속성에서 광고 선택을 삭제하기 전 최소 시간 | 24시간 |
최대 저장소 광고 선택 수 | 미정 | |
보고 출력 URL의 최대 크기 | 미정 | |
노출 보고 최대 시간 | 미정 | |
알림 호출의 최대 재시도 횟수 | 미정 | |
연결 시간 제한 | 5초 | |
reportImpression 의 최대 전체 실행 시간 |
2초 | |
reportImpressions 의 최대 호출률 |
1QPS | |
이벤트 보고 | 입찰당 구매자별 최대 비콘 수 | 10 |
입찰당 판매자별 최대 비콘 수 |
10 |
|
이벤트 키 최대 크기 |
40바이트 |
|
이벤트 데이터 최대 크기 |
64KB |
|
광고 | 광고 목록의 최대 크기 | 단일 CA의 모든 AdData 에서 공유하는 경우 10KB, 컨텍스트의 경우 미정 |
URL | 입력으로 사용된 URL 문자열의 최대 길이 | 미정 |
JavaScript | 최대 실행 시간 | 노출 보고의 입찰 및 점수 관련 1초 |
사용한 최대 메모리 | 10MB |
버그 및 문제 신고
여러분의 의견은 Android의 개인 정보 보호 샌드박스에서 매우 중요한 부분입니다. 발견한 문제나 Android의 개인 정보 보호 샌드박스 개선을 위한 아이디어가 있다면 Google에 알려 주세요.
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- Protected Audience API를 사용한 맞춤 잠재고객 타겟팅 지원
- 출시 노트
- Protected Audience: 통합 가이드