입찰 어댑터 개발

이 가이드는 Google 미디에이션 내에서 실시간 입찰 (RTB)에 참여하기 위해 입찰 어댑터를 만들려는 광고 네트워크를 대상으로 합니다. 게시자는 게시자 미디에이션 안내를 참고하세요.

입찰 어댑터는 통합에서 클라이언트 측 부분입니다. 어댑터를 사용하면 광고 네트워크 SDK가 Google 모바일 광고 SDK와 통신하여 입찰자가 게재하는 광고를 로드할 수 있습니다.

입찰이 올바르게 작동하려면 어댑터에서 초기화, 신호 수집, 광고 로드, 광고 수명 주기 이벤트 릴레이를 처리해야 합니다. 이 가이드에서는 이러한 작업을 처리하기 위해 어댑터를 구현하는 방법을 설명합니다.

입찰 어댑터의 워크플로

초기화

요청, 응답, 렌더링으로 구성되는 어댑터의 전체 수명 주기는 아래에 자세히 설명되어 있습니다.

어댑터는 워크플로의 다음 부분을 담당합니다.

  • 4~7단계: 어댑터를 초기화하고 초기화가 완료되면 Google 모바일 광고 SDK를 콜백합니다.

  • 10~13단계: RTB 요청에 참여하기 위해 입찰자에게 전송할 광고 네트워크 SDK의 신호를 수집하여 Google 모바일 광고 SDK에 전달합니다.

  • 18~21단계: 입찰자가 낙찰가를 반환하면 입찰자의 응답에 따라 광고를 로드합니다. 로드가 완료되면 Google 모바일 광고 SDK에 광고가 로드되었음을 알립니다.

  • 23단계 이상: 광고가 표시되는 동안 Google 모바일 광고 SDK에 노출 및 클릭 이벤트와 함께 광고의 프레젠테이션 수명 주기 동안 발생하는 기타 광고 이벤트를 알립니다.

입찰 어댑터 구현

Google 모바일 광고 SDK용 입찰 어댑터를 만들려면 RtbAdapter 추상 클래스를 확장해야 합니다. 다음 섹션에서는 RtbAdapter의 각 추상 메서드에 대해 설명합니다.

getSDKVersionInfo()

여기서 SDK의 버전을 반환해야 합니다. 이 버전은 OpenRTB 요청의 일부로 입찰자에게 전달됩니다.

이 메서드를 사용하려면 VersionInfo를 반환해야 합니다. 아래 예는 SDK의 문자열 버전을 VersionInfo.로 변환하는 방법을 보여줍니다.

@Override
public VersionInfo getSDKVersionInfo() {
  // Get your SDK's version as a string. E.g. "1.2.3"
  // String versionString = YourSdk.getVersion();
  String splits[] = versionString.split("\\.");
  if (splits.length >= 3) {
      int major = Integer.parseInt(splits[0]);
      int minor = Integer.parseInt(splits[1]);
      int micro = Integer.parseInt(splits[2]);
      return new VersionInfo(major, minor, micro);
   }

   String logMessage = String.format("Unexpected SDK version format: %s." +
           "Returning 0.0.0 for SDK version.", sdkVersion);
   Log.w(TAG, logMessage);
   return new VersionInfo(0, 0, 0);
}

getVersionInfo()

여기서 어댑터 버전을 반환해야 합니다. 이 버전은 OpenRTB 요청의 일부로 입찰자에게 전달됩니다.

Google의 오픈소스 및 버전이 명시된 어댑터에서는 4자리 어댑터 버전 체계를 사용하지만, VersionInfo에서는 3자리만 사용합니다. 이 문제를 해결하려면 마지막 두 자리를 패치 버전에 결합하는 것이 좋습니다(아래 참고).

@Override
public VersionInfo getVersionInfo() {
  // Get your adapters's version as a string. E.g. "1.2.3.0"
  String versionString = BuildConfig.VERSION_NAME;
  String splits[] = versionString.split("\\.");
  if (splits.length >= 4) {
      int major = Integer.parseInt(splits[0]);
      int minor = Integer.parseInt(splits[1]);
      int micro = Integer.parseInt(splits[2]) * 100 + Integer.parseInt(splits[3]);
      return new VersionInfo(major, minor, micro);
    }

    String logMessage = String.format("Unexpected adapter version format: %s." +
                "Returning 0.0.0 for adapter version.", versionString);
    Log.w(TAG, logMessage);
    return new VersionInfo(0, 0, 0);
}

initialize()

제한 시간: 30초

initialize() 메서드는 어댑터에서 호출되는 첫 번째 메서드입니다. 세션당 한 번만 호출됩니다. 이 메서드는 이 앱에서 광고 네트워크에 대해 구성된 게재위치의 전체 목록을 나타내는 MediationConfiguration 객체 목록을 제공합니다. 이 목록을 순환하여 각 게재위치의 사용자 인증 정보를 파싱하고, 초기화를 위해 관련 데이터를 SDK에 전달할 수 있습니다.

SDK가 초기화되고 광고 요청을 받을 준비가 되면 InitializationCompleteCallbackonInitializationSucceeded() 메서드를 호출합니다. 이 콜백은 광고를 로드할 수 있음을 알 수 있도록 앱 게시자에게 전달됩니다.

@Override
public void initialize(Context context,
    InitializationCompleteCallback initializationCompleteCallback,
    List<MediationConfiguration> mediationConfigurations) {
  // Initialize your ad network's SDK.
  ...

  // Invoke the InitializationCompleteCallback once initialization completes.
  initializationCompleteCallback.onInitializationSucceeded();
}

collectSignals()

제한 시간: 1초

게시자가 광고를 요청할 때마다 새 RtbAdapter 인스턴스가 만들어지고 collectSignals() 메서드가 호출됩니다. 이러한 RtbAdapter 인스턴스는 해당 광고에 대한 광고 요청, 응답 및 렌더링 수명 주기 동안 사용됩니다. collectSignals() 메서드를 사용하면 어댑터가 OpenRTB 요청에서 입찰자에게 전송할 신호를 기기에서 제공할 수 있습니다.

collectSignals()는 백그라운드 스레드에서 호출됩니다. Google 모바일 광고 SDK는 입찰에 참여하는 모든 어댑터의 신호를 동시에 요청합니다. 이 점을 고려하여 이 시간에는 UI 스레드에 대한 호출을 제한하세요. 어댑터 또는 SDK에서 신호를 수집하기 위해 실행해야 하는 대용량 작업은 initialize() 메서드에서 처리되고 캐시되어야 합니다.

신호가 준비되었으면 인코딩된 신호로 onSuccess() 콜백을 호출합니다.

다음은 구현의 예입니다.

@Override
public void collectSignals(RtbSignalData rtbSignalData,
                           SignalCallbacks signalCallbacks) {
  String signals = YourSdk.getSignals();
  signalCallbacks.onSuccess(signals);
}

어댑터가 신호를 수집하지 못하면 발생한 오류를 설명하는 문자열로 signalCallbacks.onFailure()를 호출합니다.

광고 로드 메서드 구현

제한 시간: 10초

입찰자가 낙찰가를 반환하면 Google 모바일 광고 SDK가 어댑터를 호출하여 낙찰된 광고를 로드하도록 하고, SDK가 해당 광고를 로드하는 데 필요한 입찰자가 반환한 모든 데이터를 전달합니다.

호출되는 정확한 로드 메서드는 이 요청의 광고 형식에 따라 다릅니다.

광고 형식 로드 메서드
배너 loadBannerAd()
전면 광고 loadInterstitialAd()
리워드 제공됨 loadRewardedAd()

어댑터에서 지원하는 광고 형식에 대해 이러한 메서드를 구현합니다.

로드 메서드는 신호를 제공한 어댑터의 동일한 인스턴스에서 UI 스레드에서 호출됩니다. 이 메서드에서는 다음과 같은 매개변수를 제공합니다.

  • MediationAdConfiguration: SDK가 낙찰가에 맞춰 광고를 로드하는 데 필요한 매개변수가 포함되어 있습니다(예: 입찰가 응답, 게시자가 AdMob UI에서 구성한 사용자 인증 정보).

  • MediationAdLoadCallback: 로드의 성공 또는 실패 여부를 Google 모바일 광고 SDK에 알리는 데 사용됩니다.

SDK가 광고를 로드하면 mediationAdLoadCallback.onSuccess()를 호출합니다. 광고 로드에 실패하면 발생한 오류를 설명하는 문자열로 mediationAdLoadCallback.onFailure()를 호출합니다.

mediationAdLoadCallback.onSuccess() 메서드를 사용하려면 Google 모바일 광고 SDK에서 정의한 '광고' 인터페이스 중 하나에 해당하는 객체를 전달해야 합니다. 이러한 광고 인터페이스에서는 광고에 대한 정보를 요청합니다.

MediationAdConfiguration에는 PNG 이미지를 나타내는 base64로 인코딩된 문자열을 반환하는 getWatermark() 메서드도 있습니다. 이 이미지는 광고의 투명한 오버레이에 타일 형식으로 표시되어야 합니다. 워터마크를 렌더링하는 방법에 관한 추가 안내는 Google에 문의하세요. 여기에는 게시자가 표시된 광고의 소스를 결정하기 위해 사용할 수 있도록 표시되는 광고에 대한 메타데이터가 포함됩니다.

배너의 경우 배너 보기를 제공하라는 메시지가 표시됩니다. 전면 광고 및 보상형 광고의 경우 나중에 광고를 게재하기 위한 show() 메서드를 구현하라는 메시지가 표시됩니다. 광고 로드를 실행하는 클래스가 이러한 광고 메서드도 구현하도록 하는 것이 좋습니다.

다음은 loadBannerAd()의 샘플 구현입니다. 어댑터가 다른 SDK와 통합되므로 어댑터의 구현이 다르게 표시됩니다.

public final class SampleRtbAdapter extends RtbAdapter {
  ...

  @Override
  public void loadBannerAd(
      MediationBannerAdConfiguration adConfiguration,
      MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> callback) {

      SampleBannerRenderer bannerRenderer =
          new SampleBannerRenderer(adConfiguration, callback);
      bannerRenderer.render();
    }
}

// Renders a banner ad, and forwards callbacks to the Google Mobile Ads SDK.
public class SampleBannerRenderer implements MediationBannerAd {
  private MediationBannerAdConfiguration adConfiguration;
  private final MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> adLoadCallback;
  private AdView adView;
  private MediationBannerAdCallback callback;

  public SampleRtbBannerRenderer(
      MediationBannerAdConfiguration adConfiguration,
      MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> adLoadCallback) {
    this.adConfiguration = adConfiguration;
    this.adLoadCallback = adLoadCallback;
  }

  public void render() {
    adView = new AdView(adConfiguration.getContext());
    adView.setAdSize(adConfiguration.getAdSize());
    // serverParameters are the parameters entered in the AdMob UI for your network.
    adView.setAdUnitId(adConfiguration.getServerParameters().getString("adUnitId"));

    // Map the callbacks from your SDK to Google's SDK.
    adView.setAdListener(new AdListener() {
      // See the next step for more information on callback mapping.
      // ...
    });

    // Get the bid response and watermark from the ad configuration and
    // pass the relevant information to your SDK.
    String ad = adConfiguration.getBidResponse();
    String watermark = adConfiguration.getWatermark();
    Bundle extras = new Bundle();
    extras.putString("bid", ad);
    extras.putString("watermark", watermark);
    AdRequest request = new AdRequest.Builder()
        .addNetworkExtrasBundle(AdMobAdapter.class, extras)
        .build();
    adView.loadAd(request);
  }

  // MediationBannerAd implementation

  @NonNull
  @Override
  public View getView() {
    return adView;
  }
}

광고 프레젠테이션 수명 주기 이벤트 릴레이

어댑터의 가장 중요한 작업은 Google 모바일 광고 SDK에 모든 프레젠테이션 수명 주기 이벤트를 알려서 게시자에게 전달할 수 있도록 하는 것입니다. 게시자는 광고를 게재하는 광고 네트워크와 관계없이 특정 시간에 이러한 콜백을 예상하므로 Google 모바일 광고 SDK가 콜백을 게시자에게 전달할 수 있도록 적시에 최대한 많은 콜백을 호출하는 것이 중요합니다.

어댑터는 해당하는 경우 다음 이벤트를 호출해야 합니다.

모든 형식에 공통
메서드 호출 시점
reportAdClicked() 광고가 클릭되었습니다.
reportAdImpression() 광고에서 노출을 렌더링한 경우
onAdOpened() 광고가 전체 화면으로 표시된 경우
onAdClosed() 광고의 전체 화면 보기가 닫힌 경우
onAdLeftApplication() 광고로 인해 사용자가 앱에서 나간 경우
보상형 광고
onRewarded() 사용자에게 보상이 제공됨
동영상 콜백 (보상형 및 네이티브 광고)
onVideoStarted() 광고 동영상이 시작된 경우
onVideoCompleted() 광고 동영상이 완료된 경우

어댑터는 mediationAdLoadCallback.onSuccess()를 호출한 직후 MediationAdLoadCallback<MediationAdT, MediationAdCallbackT> 객체를 다시 가져옵니다. 어댑터는 이 객체를 유지하고 이를 사용하여 광고에서 발생하는 프레젠테이션 이벤트를 호출합니다.

일반적으로 이러한 이벤트는 대부분 광고 네트워크의 SDK에서 실행됩니다. 어댑터의 역할은 단순히 광고 네트워크 SDK의 콜백을 Google 모바일 광고 SDK로 매핑하는 것입니다.

다음 예에서는 SDK의 광고 리스너에서 Google 모바일 광고 SDK로 콜백을 전달하는 방법을 보여줍니다.

adView.setAdListener(new AdListener() {
    public void onAdLoaded() {
        callback = adLoadCallback.onSuccess(SampleBannerRenderer.this);
    }

    public void onAdImpression() {
        if (callback != null) {
            callback.reportAdImpression();
        }
    }

    public void onAdFailedToLoad(LoadAdError adError) {
        adLoadCallback.onFailure("Error: " + adError.toString());
    }

    public void onAdClosed() {
        if (callback != null) {
            callback.onAdClosed();
        }
    }

    public void onAdOpened() {
        if (callback != null) {
            callback.onAdOpened();
            callback.reportAdClicked();
        }
    }

    public void onAdLeftApplication() {
        if (callback != null) {
            callback.onAdLeftApplication();
        }
    }
});

네이티브 광고 노출 추적에 필요한 애셋

Google 모바일 광고 SDK는 광고가 1픽셀 표시될 때 네이티브 광고의 노출을 기록합니다. 광고 네트워크 SDK에서 유효한 노출을 렌더링하기 위해 특정 애셋을 표시해야 하는 경우 입찰자는 입찰 응답에서 이러한 필수 네이티브 애셋을 표시할 수 있습니다. 그러면 Google 모바일 광고 SDK에서 노출을 기록하기 전에 필수 네이티브 애셋이 표시되는지 확인합니다.

입찰 응답에서 추가 필수 애셋을 지정하는 방법에 대한 자세한 내용은 네이티브 필수 애셋 문서를 참고하세요.

광고 오류 표시

전면 광고 및 보상형 광고와 같은 전체 화면 형식의 경우, Google 모바일 광고 SDK가 어댑터에 광고를 표시하도록 요청할 수 있도록 MediationInterstitialAd 또는 MediationRewardedAd 구현을 성공 로드 콜백에 제공합니다.

Google 모바일 광고 SDK는 어댑터가 광고를 성공적으로 로드한 경우 게시자가 광고 표시를 요청할 때 광고를 게재할 준비가 된 것으로 예상합니다. 즉, 쇼를 호출할 때마다 노출이 발생해야 합니다.

그러나 광고를 게재할 수 없는 경우도 있습니다. 광고를 표시할 수 없는 경우 onAdFailedToShow() 콜백을 호출하여 노출을 취소합니다.

아래의 표에는 프레젠테이션 콜백이 전체 화면 광고 형식의 노출 기록에 미치는 영향이 나와 있습니다.

콜백 결과
onAdOpened() Impression recorded
onAdFailedToShow() Impression failure1
해당 사항 없음(몇 초) Impression recorded

1 실패한 노출의 경우 광고 네트워크에 노출에 대한 요금이 청구되지 않지만 청구 가능 이벤트 요율 조정에 영향을 미칩니다. 자세한 내용은 입찰 요청 신호를 참고하세요.

다음은 광고 프로그램 호출로 인해 오류가 발생할 수 있는 로드/프로그램 수명 주기를 보여주는 모의 예시입니다.

final class SampleRtbAdapter extends RtbAdapter implements MediationRewardedAd {

 private MediationRewardedAdCallback callback;
 private RewardedAd rewardedAd;

 ...

  @Override
  public void loadRewardedAd(
      MediationRewardedAdConfiguration adConfiguration,
      final MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback> loadCallback) {

    // Load an ad. This mock example uses Google's SDK, but in practice
    // your adapter will load the ad using your ad network's SDK.
    RewardedAd.load(adConfiguration.getContext(),
        "ca-app-pub-3940256099942544/5224354917",
        new AdRequest.Builder().build(),
        new RewardedAdLoadCallback() {
          @Override
          public void onAdLoaded(@NonNull RewardedAd rewardedAd) {
            // When the ad loads, invoke the load success callback.
            callback = loadCallback.onSuccess(SampleRtbAdapter.this);
          }
        });
  }

  @Override
  public void showAd(Context context) {
    // In this mock example, your ad network requires an activity context, but
    // didn't receive one, making you unable to show the ad.
    if (!(context instanceof Activity)) {
      AdError error = new AdError(1, "Context must be an activity",
          "com.google.ads.mediation.sample");
      callback.onAdFailedToShow(error);
    }

    // This example shows Google SDK's callbacks, but it's likely your SDK
    // has similar presentation callbacks.
    rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() {
      @Override
      public void onAdShowedFullScreenContent() {
        // Your ad network SDK successfully showed the ad. Call onAdOpened().
        callback.onAdOpened();
      }

      @Override
      public void onAdFailedToShowFullScreenContent(AdError adError) {
        // Your ad network SDK failed to show the ad, invoke onAdFailedToShow.
        // In practice, you will map your SDK's error to an AdError.
        AdError error = new AdError(adError.getCode(), adError.getMessage(),
            adError.getDomain());
        callback.onAdFailedToShow(adError);
      }
    });


    rewardedAd.show((Activity) context, ...);
  }
}