Protected App Signals 개발자 가이드

개발자가 Protected App Signals API로 실험을 시작할 수 있도록 이 문서에서는 API 노출 영역 내의 모든 API를 설명하고 테스트 환경을 설정하는 방법을 안내하고 구성 및 스크립트 예시를 제공합니다.

버전 기록

2024년 1월

PAS MVP 출시를 지원하는 개발자 가이드의 첫 번째 버전

2024년 3월

Android API의 M-2024-05 출시와 서버 측 구성요소의 2024년 4월 출시를 지원하도록 API가 변경되었습니다. 주요 변경사항은 다음과 같습니다.

  • 온디바이스 API에 필요한 권한에 관한 세부정보를 추가했습니다.
  • 기기 내 신호 할당량 관리에 관한 세부정보가 추가되었습니다.
  • 문맥 광고 검색 및 이그레스 지원과 관련된 변경사항으로 업데이트된 generateBid 서명
  • 이그레스 지원을 포함하여 reportWin 문서가 업데이트되었습니다.
  • BYOS 광고 검색에 대한 지원을 삭제하고 광고 검색 UDF를 문서화하는 Ad Retrieval API 문서가 업데이트되었습니다.

API 개요

Protected Signals API 노출 영역에는 여러 시스템의 다양한 API 하위 집합이 포함되어 있습니다.

  • Android API:
    • Signal Curation API: 다음으로 구성됩니다.
    • Update Signals API
    • Signals Encoding API
    • Protected Auction Support API: SDK가 보호된 앱 신호를 사용하여 입찰 서버에서 보호된 입찰을 실행하는 데 사용합니다.
  • 서버 측 API:
    • Protected Auction API: 입찰 서버에서 실행되는 일련의 JS 스크립트입니다. 이 API를 사용하면 판매자와 구매자가 보호된 입찰을 구현하는 로직을 작성할 수 있습니다.
    • Ad Retrieval API: 구매자의 입찰 서버에서 사용할 수 있는 컨텍스트 및 사용자 정보를 고려하여 후보 광고 목록을 제공합니다.

Android 클라이언트

클라이언트 측에서 보호된 앱 신호 노출 영역은 세 가지 API로 구성됩니다.

  • Update Signals: 기기에서 신호의 선별을 사용 설정하는 Android 시스템 API입니다.
  • Signals Encoding: 입찰 중에 서버로 전송될 신호를 준비하는 JavaScript API입니다.
  • Protected Auction Support: 입찰 서버에서 보호된 입찰의 실행을 지원하는 API입니다. 이 API는 보호된 앱 신호에만 국한되지 않으며 Protected Audience API 입찰을 지원하는 데도 사용됩니다.

Update Signals API

Update Signals API는 애드테크에 구매자를 대신하여 사용자 및 앱 관련 신호를 등록할 수 있는 기능을 제공합니다. 이 API는 위임 모델에서 작동합니다. 호출자는 프레임워크가 상응하는 신호를 가져올 URI와 이러한 신호가 입찰에서 사용되도록 인코딩하는 로직을 제공합니다.

API에는 android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS 권한이 필요합니다.

updateSignals() API는 추가하거나 삭제할 신호와 입찰을 위해 이러한 신호를 준비하는 방법을 설명하는 JSON 객체를 URI에서 검색합니다.

Executor executor = Executors.newCachedThreadPool();
ProtectedSignalsManager protectedSignalsManager
     =  ProtectedSignalsManager.get(context);

// Initialize a UpdateSignalsRequest
UpdateSignalsRequest updateSignalsRequest = new
  UpdateSignalsRequest.Builder(Uri.parse("https://example-adtech1.com/signals"))
      .build();

OutcomeReceiver<Object, Exception> outcomeReceiver = new OutcomeReceiver<Object, Exception>() {
  @Override
  public void onResult(Object o) {
    //Post-success actions
  }

  @Override
  public void onError(Exception error) {
    //Post-failure actions
  };

// Call updateSignals
protectedSignalsManager.updateSignals(updateSignalsRequest,
    executor,
    outcomeReceiver);

플랫폼은 요청에 제공된 URI에 HTTP 요청을 보내 신호 업데이트를 가져옵니다. 응답에는 신호 업데이트에 더해 원시 신호를 인코딩된 페이로드로 변환하는 인코딩 로직을 호스팅하는 엔드포인트가 포함될 수 있습니다. 신호 업데이트는 JSON 형식이어야 하며, 다음 키를 포함할 수 있습니다.

JSON 객체의 최상위 수준 키는 다음 5가지 명령어 중 하나와 일치해야 합니다.

설명

put

새 신호를 추가하여 동일한 키로 기존 신호를 덮어씁니다. 값은

JSON 객체입니다. 여기서 키는 입력할 키에 해당하는 base64 문자열이고 값은 입력할 값에 해당하는 base64 문자열입니다.

append

새 신호를 시계열 신호에 추가하고,

계열의 크기가 지정된 최댓값을 초과하면 새 신호를 위한 공간을 확보하기 위해 가장 오래된 신호를 삭제합니다. 값은 JSON 객체입니다. 여기서 키는 추가할 키에 해당하는 base64 문자열이고 값은 'values' 및 'maxSignals'라는 두 개의 필드를 갖는 객체입니다.

'values': 시계열에 추가할 신호 값에 해당하는 base64 문자열 목록입니다.

'maxSignals': 이 시계열에서 허용되는 값의 최대 개수입니다. 키와 연결된 현재 신호 수가

maxSignals를 초과하는 경우 가장 오래된 신호가 삭제됩니다. put을 사용하여 추가된 키에 추가할 수 있습니다. 최대 개수보다 많은 값을 추가하면 오류가 발생합니다.

put_if_not_present

동일한 키를 가진 기존 신호가 없는 경우에만 새 신호를 추가합니다. 값은 JSON 객체입니다. 여기서 키는 입력할 키에 해당하는 base64 문자열이고 값은 입력할 값에 해당하는 base64 문자열입니다.

remove

키의 신호를 삭제합니다. 값은 삭제해야 하는 신호의 키에 해당하는 base64 문자열 목록입니다.

update_encoder

엔드포인트를 업데이트하는 작업 및 인코딩 로직을 검색하는 데 사용할 수 있는

URI를 제공합니다. 업데이트 작업을 제공하기 위한 하위 키는 'action'이고

현재 지원되는 값은 처음 제공된 경우 인코더 엔드포인트를 등록하거나 기존 엔드포인트를 새로 제공된 엔드포인트로 덮어쓰는 'REGISTER'입니다. 'REGISTER' 작업에는 엔드포인트를 제공해야 합니다. 인코더 엔드포인트를 제공하기 위한 하위 키는 'endpoint'이고 값은 엔드포인트의

URI 문자열입니다.

다음은 샘플 JSON 요청입니다.

{
    "put": {
        "AAAAAQ==": "AAAAZQ==",
        "AAAAAg==": "AAAAZg=="
    },
    "append": {
        "AAAAAw==": {
            "values": [
                "AAAAZw=="
            ],
            "max_signals": 3
        }
    },
    "put_if_not_present": {
        "AAAABA==": "AAAAaQ==",
        "AAAABQ==": "AAAAag=="
    },
    "update_encoder": {
        "action": "REGISTER",
        "endpoint": "https://adtech1.com/Protected App Signals_encode_script.js"
    }
}

신호에는 10~15Kb 단위의 기기 내 할당량이 있습니다. 할당량이 초과되면 PPAPI는 FIFO 전략을 사용하여 신호를 제거합니다. 제거 프로세스를 사용하면 제거 빈도를 줄이기 위해 짧은 시간 동안 할당량을 약간 초과할 수 있습니다.

Signals Encoding API

구매자는 보호된 입찰 중에 서버로 전송할 기기에 저장된 신호를 인코딩하는 데 사용할 JavaScript 함수를 제공해야 합니다. 구매자는 UpdateSignal API 요청에 대한 임의의 응답에서 'update_encoder' 키를 사용하여 가져올 수 있는 URL을 추가하여 이 스크립트를 제공할 수 있습니다. 스크립트는 다음과 같은 서명을 갖습니다.

function encodeSignals(signals, maxSize) {
  let result = new Uint8Array(maxSize);
  // first entry will contain the total size
  let size = 1;
  let keys = 0;
  
  for (const [key, values] of signals.entries()) {
    keys++;
    // In this encoding we only care about the first byte
    console.log("key " + keys + " is " + key)
    result[size++] = key[0];
    result[size++] = values.length;
    for(const value of values) {
      result[size++] = value.signal_value[0];
    }
  }
  result[0] = keys;
  
  return { 'status': 0, 'results': result.subarray(0, size)};
}

signals 매개변수는 크기가 4인 UInt8Arrays 형식의 키를 보호된 앱 신호 객체 목록에 매핑하는 맵입니다. 각 보호된 앱 신호 객체는 3개의 필드를 갖습니다.

  • signal_value: 신호의 값을 나타내는 UInt8Array입니다.
  • creation_time: 신호의 생성 시간을 에포크 초 단위로 나타내는 숫자입니다.
  • package_name: 신호를 만든 패키지의 이름을 나타내는 문자열입니다.

maxSize 매개변수는 출력에 허용되는 가장 큰 배열 크기를 나타내는 숫자입니다.

이 함수는 2개의 필드를 갖는 객체를 출력합니다.

  • status: 스크립트가 성공적으로 실행된 경우 0입니다.
  • results: 길이가 maxSize 이하인 UInt8Array입니다. 이 배열은 입찰 중에 서버로 전송되며 prepareDataForAdRetrieval 스크립트에 의해 준비됩니다.

인코딩은 애드테크에 자체 맞춤 로직에 따라 원시 신호를 연결된 버전으로 압축하는 등의 변환을 실행할 수 있는 특성 추출의 초기 단계를 제공합니다. 신뢰할 수 있는 실행 환경(TEE)에서 실행되는 보호된 입찰이 진행되는 동안 애드테크 맞춤 로직은 인코딩에 의해 생성된 신호 페이로드에 대한 읽기 액세스 권한을 갖습니다. 구매자의 B&A TEE에서 실행되는 사용자 정의 함수(UDF)라고 하는 맞춤 로직은 게시자 앱에서 광고 선택(광고 검색 및 입찰)을 수행하기 위해 제공하는 인코딩된 신호 및 기타 문맥 시그널에 대한 읽기 액세스를 갖습니다.

신호 인코딩

등록된 신호로 인코딩 로직을 제공한 구매자는 매시간 신호가 입찰 페이로드로 인코딩됩니다. 입찰 페이로드의 바이트 배열은 기기에서 유지되고, 암호화된 후 판매자가 광고 선택 데이터의 일부로 수집하여 보호된 입찰의 일부로 포함됩니다. 테스트를 위해서는 다음 명령어를 실행하여 시간별 주기 외적으로 이 인코딩을 트리거할 수 있습니다.

adb shell cmd jobscheduler run -f com.google.android.adservices.api 29
인코더 로직 버전 관리

애드테크 맞춤 인코더 로직 다운로드 요청이 이루어지면 애드테크 엔드포인트는 응답 헤더에 버전 번호를 지정하여 응답할 수 있습니다. 이 버전은 기기의 인코더 로직과 함께 유지됩니다. 원시 신호가 인코딩될 때 인코딩된 페이로드는 인코딩에 사용되는 버전과 함께 유지됩니다. 이 버전은 보호된 입찰 중에 입찰 서버에 함께 전송되므로 애드테크가 이 버전을 기준으로 입찰 및 인코딩 로직을 조정할 수 있습니다.

Response header for providing encoder version : X_ENCODER_VERSION

Protected Auction Support API

기기 측에서는 보호된 앱 신호를 위한 입찰을 실행하는 것은 보호된 잠재고객에 대한 입찰을 실행하는 것과 동일합니다.

입찰 서비스

서버 측 API에는 다음이 포함됩니다.

  • Protected Bidding API: 구매자와 판매자가 자신이 소유한 입찰 구성요소에 배포하여 입찰 및 입찰 로직을 결정하는 데 사용할 수 있는 일련의 JS 함수 또는 UDF입니다.
  • Ad Retrieval API: 구매자는 보호된 앱 신호 입찰을 위한 후보 광고 세트를 제공하는 REST 엔드포인트를 구현하여 이 API를 구현할 수 있습니다.

Protected Auction API

Protected Auction API는 구매자와 판매자가 입찰 및 입찰 로직을 구현하는 데 사용할 수 있는 JS API 또는 UDF로 구성됩니다.

구매자 애드테크 UDF
prepareDataForAdRetrieval UDF

보호된 앱 신호를 사용하여 TEE 광고 검색 서비스에서 광고 조합을 가져오려면 먼저 구매자가 보호된 앱 신호와 기타 판매자 제공 데이터를 디코딩하고 준비해야 합니다. 구매자 prepareDataForAdRetrieval UDF 출력이 광고 검색 서비스에 전달되어 입찰에 사용할 상위 k개 후보 광고를 검색합니다.

// Inputs
// ------
// encodedOnDeviceSignals: A Uint8Array of bytes from the device.
// encodedOnDeviceSignalsVersion: An integer representing the encoded
//   version of the signals.
// sellerAuctionSignals: Information about auction (ad format, size) derived
//                       contextually.
// contextualSignals: Additional contextual signals that could help in
//                    generating bids.
//
// Outputs
// -------
// Returns a JSON structure to be used for retrieval.
// The structure of this object is left to the adtech.
function prepareDataForAdRetrieval(encodedOnDeviceSignals,encodedOnDeviceSignalsVersion,sellerAuctionSignals,contextualSignals) {
   return {};
}
generateBid UDF

상위 k개의 후보 광고가 반환된 후 광고 후보가 구매자의 맞춤 입찰 로직인 generateBid UDF로 전달됩니다.

// Inputs
// ------
// ads: Data string returned by the ads retrieval service. This can include Protected App Signals
//   ads and related ads metadata.
// sellerAuctionSignals: Information about the auction (ad format, size),
//                       derived contextually
// buyerSignals: Any additional contextual information provided by the buyer
// preprocessedDataForRetrieval: This is the output of this UDF.
function generateBid(ads, sellerAuctionSignals, buyerSignals,
                    preprocessedDataForRetrieval,
                    rawSignals, rawSignalsVersion) {
    return { "ad": <ad Value Object>,
             "bid": <float>,
             "render": <render URL string>,
             'adCost': <optional float ad cost>,
             "egressPayload": <limitedEgressPayload>,
             "temporaryUnlimitedEgressPayload": <temporaryUnlimitedEgressPayload>
    };
}

이 함수의 출력은 광고 조합의 단일 입찰가이며 ProtectedAppSignalsAdWithBidMetadata와 동일한 JSON으로 표시됩니다. 또한 함수는 모델 학습을 사용 설정하기 위해 reportWin에 전달되는 두 개의 배열을 반환할 수도 있습니다. 이그레스 및 모델 학습에 대한 자세한 내용은 PAS 설명의 보고 섹션을 참조하세요.

reportWin UDF

입찰이 종료되면 입찰 서비스는 reportWin UDF (Protected Audience에 사용되는 것과 동일한 reportWin 함수)를 사용하여 구매자를 위한 보고 URL을 생성하고 비콘을 등록합니다. 클라이언트가 광고를 렌더링하면 기기에서 비콘을 핑합니다. 이 메서드의 서명은 모델 학습을 사용 설정하는 데 사용되고 generateBid의 결과로 채워지는 두 개의 추가 매개변수 egressPayloadtemporaryUnlimitedEgressPayload를 제외하면 Protected Audience 버전과 거의 동일합니다.

// Inputs / Outputs
// ----------------
// See detailed documentation here.
function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                   buyerReportingSignals,
                   egressPayload, temporaryUnlimitedEgressPayload) {
  // ...
}
판매자 애드테크 UDF
scoreAd UDF

이 UDF는 판매자가 구매자로부터 수신한 광고 중 입찰에서 낙찰될 광고를 선택하는 데 사용됩니다.

function scoreAd(adMetadata, bid, auctionConfig,
                 trustedScoringSignals, bid_metadata) {
  // ...
  return {desirability: desirabilityScoreForThisAd,
              allowComponentAuction: true_or_false};
}
reportResult UDF

이 UDF를 사용하면 판매자가 낙찰된 광고에 관한 정보를 사용하여 이벤트 수준 보고를 수행할 수 있습니다.

function reportResult(auctionConfig, reporting_metadata) {
  // ...
  registerAdBeacon({"click", clickUrl,"view", viewUrl});
  sendReportTo(reportResultUrl);
  return signalsForWinner;
}

Ad Retrieval API

MVP 출시에서 광고 검색 서비스는 구매자가 관리하는 호스팅 서비스이며 입찰 서비스가 이 서비스에서 광고 조합을 검색합니다. 2024년 4월부터 광고 검색 서버는 신뢰할 수 있는 실행 환경 (TEE)에서 실행되어야 하며 GRPC/proto 인터페이스를 노출합니다. 애드테크 회사는 이 서버를 설정하고 입찰 스택 배포의 일부로 URL을 제공해야 합니다. TEE에서 실행되는 이 서비스의 구현은 개인 정보 보호 샌드박스 GitHub에서 확인할 수 있으며 문서의 나머지 부분에서는 이것이 배포에 사용된 코드라고 가정합니다.

2024년 4월부터 B&A 버전에서 문맥 경로 광고 검색을 지원합니다. 이 경우 입찰 서버는 입찰의 문맥적 부분에서 RTB 서버에서 전송한 광고 식별자 목록을 수신합니다. 식별자는 TEE KV 서버로 전송되어 입찰 단계에서 사용할 모든 광고 관련 정보(예: Top-K 선택에 사용할 광고 렌더링 URL, 메타데이터, 광고 임베딩)를 가져옵니다. 이 두 번째 경로에는 배포할 특정 로직이 필요하지 않으므로 여기서는 TEE 기반 광고 검색 사용 사례를 구성하는 방법만 설명합니다.

HandleRequest UDF
function HandleRequest(requestMetadata, preparedDataForAdRetrieval,
                      deviceMetadata, contextualSignals) {
    return adsMetadataString;
}

각 항목의 의미는 다음과 같습니다.

  • requestMetadata: JSON입니다. UDF에 대한 요청별 서버 메타데이터입니다. 지금은 비어 있습니다.
  • preparedDataForAdRetrieval: 이 필드의 콘텐츠는 광고 검색 전략에 따라 다릅니다. 문맥 광고 검색의 경우 이 매개변수에 기기에서 시작되고 입찰 서비스에서 전달된 원시 신호가 포함됩니다. 광고 검색 서버를 사용하는 TEE 광고 검색의 경우 이 매개변수에 prepareDataForAdRetrieval UDF의 결과가 포함됩니다. 참고: 이 단계에서 보호된 앱 신호는 디코딩 및 암호화되지 않습니다.
  • deviceMetadata: 판매자의 광고 서비스에서 전달한 기기 메타데이터가 포함된 JSON 객체입니다. 자세한 내용은 B&A 문서를 참고하세요.
    • X-Accept-Language: 기기에서 사용되는 언어입니다.
    • X-User-Agent: 기기에서 사용되는 사용자 에이전트입니다.
    • X-BnA-Client-IP: 기기 IP 주소입니다.
  • contextualSignals: 동일한 DSP에서 운영하는 문맥 입찰 서버에서 가져온 임의의 문자열입니다. UDF는 문자열을 디코딩하여 사용할 수 있어야 합니다. 문맥 신호에는 보호된 앱 신호를 사용하여 전달된 보호된 임베딩의 ML 모델 버전 정보와 같은 정보가 포함될 수 있습니다.

UDF는 성공 시 문자열을 반환해야 합니다. 문자열은 입찰 서버로 반환되며 입찰 서버는 generateBid UDF로 전달됩니다. 문자열은 단순한 문자열일 수 있지만 문자열은 각 광고 기술에서 자체적으로 스키마를 정의하는 직렬화된 객체일 가능성이 높습니다. 광고 기술의 generateBid 로직이 문자열을 인식하고 사용할 수 있는 한 스키마에 제약 조건이 없습니다.

개발용으로 시스템 설정

Android

Android 개발 환경을 설정하려면 다음을 실행해야 합니다.

  1. 개발자 프리뷰 10 이미지를 실행하는 에뮬레이터(권장) 또는 실제 기기 만들기
  2. 다음을 실행합니다.
adb shell am start -n com.google.android.adservices.api/com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity

그런 다음 앱 추천 광고에 동의하는 옵션을 선택합니다.

  1. 다음 명령어를 실행하여 관련 API를 사용 설정합니다. 사용 중지 기본 구성이 주기적으로 동기화되므로 이를 때때로 다시 실행해야 할 수 있습니다.
adb shell device_config put adservices fledge_custom_audience_service_kill_switch false;  adb shell device_config put adservices fledge_select_ads_kill_switch false; adb shell device_config put adservices fledge_on_device_auction_kill_switch false; adb shell device_config put adservices fledge_auction_server_kill_switch false; adb shell "device_config put adservices disable_fledge_enrollment_check true";  adb shell device_config put adservices ppapi_app_allow_list '\*'; adb shell device_config put adservices fledge_auction_server_overall_timeout_ms 60000;
  1. 기기를 다시 시작합니다.
  2. 입찰 키 서버를 가리키도록 기기의 입찰 키를 재정의합니다. 잘못된 키가 캐시되지 않도록 입찰을 실행하기 전에 이 단계를 실행하는 것이 중요합니다.

입찰 서비스

입찰 서버를 설정하려면 셀프 서비스 설정 문서를 참고하세요.

이 문서에서는 구매자별 서버를 구성하는 방법을 중점적으로 다룹니다. 판매자가 변경할 사항은 없습니다.

기본 요건

입찰 서비스 스택을 배포하기 전에 구매자 애드테크는 다음을 실행해야 합니다.

  • 자체 TEE 광고 검색 서비스를 배포했는지 확인합니다 (관련 섹션 참고).
  • 광고 기술에 필요한 모든 UDF(prepareDataForAdRetrieval, generateBid, reportWin, HandleRequest)가 정의되어 호스팅되어 있는지 확인합니다.

보호된 입찰 및 보호된 잠재고객이 입찰 서비스와 작동하는 방식을 이해하는 것은 도움이 되지만 필수는 아닙니다.

Terraform 구성

보호된 앱 신호를 사용하려면 애드테크는 다음을 충족해야 합니다.

  • 입찰 서버에서 보호된 앱 신호 지원을 사용 설정합니다.
  • prepareDataForAdRetrieval, generateBidreportWin의 새 UDF를 가져올 수 있는 URL 엔드포인트를 제공합니다.

또한 이 가이드에서는 리마케팅에 입찰 서버를 사용하려는 애드테크가 리마케팅 입찰에 대한 기존의 모든 구성 플래그를 평소와 같이 계속 설정한다고 가정합니다.

구매자 애드테크 구성

구매자는 이 데모 파일을 예시로 참고하여 다음 플래그를 설정해야 합니다.

  • 보호된 앱 신호 사용 설정: 보호된 앱 신호 데이터를 수집하도록 사용 설정합니다.
  • 보호된 앱 신호 URL: 보호된 앱 신호 서버의 URL로 설정합니다.

애드테크는 다음 필드에서 자리표시자의 올바른 URL을 대체해야 합니다.

module "buyer" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"
    PROTECTED_APP_SIGNALS_GENERATE_BID_TIMEOUT_MS = "60000"
    TEE_AD_RETRIEVAL_KV_SERVER_ADDR               = "<service mesh address of the instance>"
    AD_RETRIEVAL_TIMEOUT_MS                       = "60000"
    BUYER_CODE_FETCH_CONFIG                       = <<EOF
    {
        "protectedAppSignalsBiddingJsUrl": "<URL to Protected App Signals generateBid UDF>",
        "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
        "urlFetchPeriodMs": 13000000,
        "prepareDataForAdsRetrievalJsUrl": "<URL to the UDF>"
    }
    EOF

  }  # runtime_flags

}  # Module "buyer"

판매자 애드테크 구성

판매자는 이 데모 파일을 예시로 사용하여 다음 플래그를 설정해야 합니다. (참고: 여기에는 보호된 앱 신호 관련 구성만 강조표시되어 있습니다.) 애드테크는 자리표시자의 올바른 URL을 대체해야 합니다.

module "seller" {
  # ... More config here.

  runtime_flags = {
    # ... More config here.

    ENABLE_PROTECTED_APP_SIGNALS                  = "true"

    SELLER_CODE_FETCH_CONFIG                           = <<EOF
  {
    "urlFetchTimeoutMs": 60001, # This has to be > 1 minute.
    "urlFetchPeriodMs": 13000000,
    "protectedAppSignalsBuyerReportWinJsUrls": {"<Buyer Domain>": "URL to reportWin UDF"}
  }
  EOF

  }  # runtime_flags

}  # Module "seller"

KV 및 광고 검색 서비스

광고 검색을 지원하기 위해 선택한 전략에 따라 시스템은 KV 서비스 인스턴스를 한두 개 배포해야 합니다. TEE 기반 광고 검색에 사용되는 KV 인스턴스 인스턴스를 Ad Retrieval Server로, 컨텍스트 경로 기반 검색을 KV Lookup Server로 지원하기 위해 인스턴스를 지칭합니다.

두 경우 모두 서버 배포는 KV 서버 GitHub에서 제공되는 문서를 따릅니다. 두 경우의 차이점은 조회 사례가 추가 구성 없이 즉시 작동한다는 점입니다. 검색 논리를 구현하려면 검색 로직을 구현하기 위해 HandleRequest UDF를 배포해야 합니다. 자세한 내용은 KV 서버 온보딩 가이드를 참고하세요. B&A에서는 두 서비스가 모두 입찰 서비스와 동일한 서비스 메시에 배포되어야 합니다.

설정 예시

다음 시나리오를 생각해 보세요. 애드테크는 Protected App Signals API를 사용하여 사용자 앱 사용에 따라 관련 신호를 저장합니다. 이 예에서는 여러 앱의 인앱 구매를 나타내는 신호가 저장됩니다. 입찰 중에 암호화된 신호가 수집되어 입찰 서버에서 실행되는 보호된 입찰로 전달됩니다. 입찰 서버에서 실행되는 구매자의 UDF는 신호를 사용하여 광고 후보를 가져오고 입찰가를 계산합니다.

[구매자] 신호 예시

키가 0이고 값이 1인 신호를 추가합니다.

{
  "put": {
    "AA==": "AQ=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

키가 1이고 값이 2인 신호를 추가합니다.

{
  "put": {
    "AQ==": "Ag=="
  },
  "update_encoder": {
    "action": "REGISTER",
    "endpoint": "https://example.com/example_script"
  }
}

[구매자] encodeSignals 예시

각 신호를 2바이트로 인코딩합니다. 첫 번째 바이트는 신호 키의 첫 번째 바이트이고 두 번째 바이트는 신호 값의 첫 번째 바이트입니다.

function encodeSignals(signals, maxSize) {
  // if there are no signals don't write a payload
  if (signals.size === 0) {
      return {};
  }

  let result = new Uint8Array(signals.size * 2);
  let index = 0;
  
  for (const [key, values] of signals.entries()) {
    result[index++] = key[0];
    result[index++] = values[0].signal_value[0];
  }
  
  return { 'status': 0, 'results': result};
}

[구매자] prepareDataForAdRetrieval 예시

/**
 * `encodedOnDeviceSignals` is a Uint8Array and would contain
 * the app signals emanating from device. For purpose of the
 * demo, in our sample example, we assume that device is sending
 * the signals with pair of bytes formatted as following:
 * "<id><In app spending>". Where id corresponds to an ad category
 * that user uses on device, and the in app spending is a measure
 * of how much money the user has spent in this app category
 * previously. In our example, id of 0 will correspond to a
 * fitness ad category and a non-zero id will correspond to
 * food app category -- though this info will be useful
 * later in the B&A pipeline.
 *
 * Returns a JSON object indicating what type of ad(s) may be
 * most relevant to the user. In a real setup ad techs might
 * want to decode the signals as part of this script.
 *
 * Note: This example script makes use of only encoded device signals
 * but adtech can take other signals into account as well to prepare
 * the data that will be useful down stream for ad retrieval and
 * bid generation. The max length of the app signals used in this
 * sample example is arbitrarily limited to 4 bytes.
 */
function prepareDataForAdRetrieval(encodedOnDeviceSignals,
                                   encodedOnDeviceSignalsVersion,
                                   sellerAuctionSignals,
                                   contextualSignals) {
  if (encodedOnDeviceSignals.length === 0 || encodedOnDeviceSignals.length > 4 ||
      encodedOnDeviceSignals.length % 2 !== 0) {
     throw "Expected encoded signals length to be an even number in (0, 4]";
  }

  var preparedDataForAdRetrieval = {};
  for (var i = 0; i < encodedOnDeviceSignals.length; i += 2) {
    preparedDataForAdRetrieval[encodedOnDeviceSignals[i]] = encodedOnDeviceSignals[i + 1];
  }
  return preparedDataForAdRetrieval;
}

[구매자] 샘플 광고 검색 UDF

이 예에서 광고 검색 서버는 상위 k개의 광고 후보 각각에 대한 메타데이터(이 예에서는 각 광고의 ID이지만 나중에 입찰을 생성하는 데 도움이 될 수 있는 다른 데이터를 포함할 수 있음)를 전송합니다.

function HandleRequest(requestMetadata, protectedSignals, deviceMetadata,
                      contextualSignals) {
 return "[{\"adId\":\"0\"},{\"adId\":\"1\"}]"

[구매자] generateBid 예시

/**
 * This script receives the data returned by the ad retrieval service
 * in the `ads` argument. This argument is supposed to contain all
 * the Protected App Signals related ads and the metadata obtained from the retrieval
 * service.
 *
 * `preparedDataForAdRetrieval` argument contains the data returned
 * from the `prepareDataForAdRetrieval` UDF.
 *
 * This script is responsible for generating bids for the ads
 * collected from the retrieval service and ad techs can decide to
 * run a small inference model as part of this script in order to
 * decide the best bid given all the signals available to them.
 *
 * For the purpose of the demo, this sample script assumes
 * that ad retrieval service has sent us most relevant ads for the
 * user and this scripts decides on the ad render URL as well as
 * what value to bid for each ad based on the previously decoded
 * device signals. For simplicity sake, this script only considers
 * 2 types of app categories i.e. fitness and food.
 *
 * Note: Only one bid is returned among all the
 * input ad candidates.
 */
function generateBid(ads, sellerAuctionSignals, buyerSignals, preparedDataForAdRetrieval) {
  if (ads === null) {
    console.log("No ads obtained from the ad retrieval service")
    return {};
  }     
        
  const kFitnessAd = "0";
  const kFoodAd = "1";
  const kBuyerDomain = "https://buyer-domain.com";
        
  let resultingBid = 0;
  let resultingRender = kBuyerDomain + "/no-ad";
  for (let i = 0 ; i < ads.length; ++i) {
    let render = "";
    let bid = 0;
    switch (ads[i].adId) {
      case kFitnessAd:
        render = kBuyerDomain + "/get-fitness-app";
        bid = preparedDataForAdRetrieval[kFitnessAd];
        break;
      case kFoodAd:
        render = kBuyerDomain + "/get-fastfood-app";
        bid = preparedDataForAdRetrieval[kFoodAd];
        break;
      default:
        console.log("Unknown ad category");
        render = kBuyerDomain + "/no-ad";
        break;
    }
    console.log("Existing bid: " + resultingBid + ", incoming candidate bid: " + bid);
    if (bid > resultingBid) {
      resultingBid = bid;
      resultingRender = render;
    }
  }
  return {"render": resultingRender, "bid": resultingBid};
}

[구매자] reportWin 예시

reportWin UDF는 구매자에게 입찰에서 낙찰되었음을 보고합니다.

function reportWin(auctionSignals, perBuyerSignals, signalsForWinner,
                                       buyerReportingSignals, directFromSellerSignals,
                                       egressPayload,
                                       temporaryUnlimitedEgressPayload) {
  sendReportTo("https://buyer-controlled-domain.com/");
  registerAdBeacon({"clickEvent":"https://buyer-controlled-domain.com/clickEvent"});
  return;
}

[판매자] KV 서버 설정

판매자는 광고 렌더링 URL에서 상응하는 점수 신호에 대한 매핑을 사용할 수 있도록 점수 신호 KV 서버를 설정해야 합니다. 예를 들어, https:/buyer-domain.com/get-fitness-apphttps:/buyer-domain.com/get-fastfood-app가 구매자에 의해 반환되는 경우, 판매자가 https://key-value-server-endpoint.com?client_type=1&renderUrls=<render-url-returned-by-the-buyer>에서 GET을 사용하여 SFE에서 쿼리할 때 다음과 같은 점수 신호 응답 예시를 가질 수 있습니다.

{
   "renderUrls" : {
      "https:/buyer-domain.com/get-fitness-app" : [
         "1",
         "2"
      ],
      "https:/buyer-domain.com/get-fastfood-app" : [
         "3",
         "4"
      ]
   }
}

[판매자] scoreAd 예시

/**
 * This module generates a random desirability score for the Protected App
 * Signals ad in this example. In a production deployment,
 * however, the sellers would want to use all the available signals to generate
 * a score for the ad.
 */
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function scoreAd(adMetadata, bid, auctionConfig,
                                   trustedScoringSignals, deviceSignals,
                                   directFromSellerSignals) {
  return {
    "desirability": getRandomInt(10000),
    "allowComponentAuction": false
  };
}

[판매자] reportResult 예시

function reportResult(auctionConfig, sellerReportingSignals, directFromSellerSignals){
  let signalsForWinner = {};
    sendReportTo("https://seller-controlled-domain.com");
    registerAdBeacon({"clickEvent":
                    "https://seller-controlled-domain.com/clickEvent"});
    return signalsForWinner;
}

샘플 앱

API를 사용하여 위에 설명된 간단한 흐름을 사용하는 앱을 만드는 방법의 예로, 이 샘플 저장소에서 확인할 수 있는 보호된 앱 신호 샘플 앱을 만들었습니다.