네이티브 광고 맞춤 이벤트

기본 요건

맞춤 이벤트 설정을 완료합니다.

네이티브 광고 요청

폭포식 구조 미디에이션 체인에서 맞춤 이벤트 광고 항목에 도달하면 맞춤 이벤트를 생성할 때 제공한 클래스 이름에 대해 loadNativeAd:adConfiguration:completionHandler: 메서드가 호출됩니다. 이 경우 이 메서드는 SampleCustomEvent에 있고 SampleCustomEventNative에서 loadNativeAd:adConfiguration:completionHandler: 메서드를 호출합니다.

네이티브 광고를 요청하려면 GADMediationAdapterloadNativeAd:adConfiguration:completionHandler:를 구현하는 클래스를 만들거나 수정하세요. GADMediationAdapter를 확장하는 클래스가 이미 있다면 이 클래스에 loadNativeAd:adConfiguration:completionHandler:를 구현합니다. 또한 GADMediationNativeAd를 구현할 새 클래스를 만듭니다.

맞춤 이벤트 예에서 SampleCustomEventGADMediationAdapter 인터페이스를 구현한 다음 SampleCustomEventNative에 위임합니다.

Swift

import GoogleMobileAds

class SampleCustomEvent: NSObject, GADMediationAdapter {

  fileprivate var nativeAd: SampleCustomEventNativeAd?

  func loadNativeAd(
    for adConfiguration: GADMediationNativeAdConfiguration,
    completionHandler: @escaping GADMediationNativeAdLoadCompletionHandler
  ) {
    self.nativeAd = SampleCustomEventNativeAd()
    self.nativeAd?.loadNativeAd(
      for: adConfiguration, completionHandler: completionHandler)
  }
}

Objective-C

#import "SampleCustomEvent.h"

@implementation SampleCustomEvent

SampleCustomEventNativeAd *sampleNativeAd;

- (void)loadNativeAdForAdConfiguration:
            (GADMediationNativeAdConfiguration *)adConfiguration
                     completionHandler:
                         (GADMediationNativeAdLoadCompletionHandler)
                             completionHandler {
  sampleNative = [[SampleCustomEventNativeAd alloc] init];
  [sampleNative loadNativeAdForAdConfiguration:adConfiguration
                             completionHandler:completionHandler];
}

SampleCustomEventNative` 는 다음 작업을 담당합니다.

  • 네이티브 광고 로드

  • GADMediationNativeAd 프로토콜 구현

  • Google 모바일 광고 SDK에 광고 이벤트 콜백 수신 및 보고

AdMob UI에서 정의된 선택적 매개변수는 광고 구성에 포함됩니다. adConfiguration.credentials.settings[@"parameter"]를 통해 매개변수에 액세스할 수 있습니다. 이 매개변수는 일반적으로 광고 네트워크 SDK가 광고 객체를 인스턴스화할 때 요구하는 광고 단위 식별자입니다.

Swift

class SampleCustomEventNativeAd: NSObject, GADMediationNativeAd {
  /// The Sample Ad Network native ad.
  var nativeAd: SampleNativeAd?

  /// The ad event delegate to forward ad rendering events to the Google Mobile
  /// Ads SDK.
  var delegate: GADMediationNativeAdEventDelegate?

  /// Completion handler called after ad load
  var completionHandler: GADMediationNativeLoadCompletionHandler?

  func loadNativeAd(
    for adConfiguration: GADMediationNativeAdConfiguration,
    completionHandler: @escaping GADMediationNativeLoadCompletionHandler
  ) {
    let adLoader = SampleNativeAdLoader()
    let sampleRequest = SampleNativeAdRequest()

    // The Google Mobile Ads SDK requires the image assets to be downloaded
    // automatically unless the publisher specifies otherwise by using the
    // GADNativeAdImageAdLoaderOptions object's disableImageLoading property. If
    // your network doesn't have an option like this and instead only ever
    // returns URLs for images (rather than the images themselves), your adapter
    // should download image assets on behalf of the publisher. This should be
    // done after receiving the native ad object from your network's SDK, and
    // before calling the connector's adapter:didReceiveMediatedNativeAd: method.
    sampleRequest.shouldDownloadImages = true
    sampleRequest.preferredImageOrientation = NativeAdImageOrientation.any
    sampleRequest.shouldRequestMultipleImages = false
    let options = adConfiguration.options
    for loaderOptions: GADAdLoaderOptions in options {
      if let imageOptions = loaderOptions as? GADNativeAdImageAdLoaderOptions {
        sampleRequest.shouldRequestMultipleImages =
          imageOptions.shouldRequestMultipleImages
        // If the GADNativeAdImageAdLoaderOptions' disableImageLoading property is
        // YES, the adapter should send just the URLs for the images.
        sampleRequest.shouldDownloadImages = !imageOptions.disableImageLoading
      } else if let mediaOptions = loaderOptions
        as? GADNativeAdMediaAdLoaderOptions
      {
        switch mediaOptions.mediaAspectRatio {
        case GADMediaAspectRatio.landscape:
          sampleRequest.preferredImageOrientation =
            NativeAdImageOrientation.landscape
        case GADMediaAspectRatio.portrait:
          sampleRequest.preferredImageOrientation =
            NativeAdImageOrientation.portrait
        default:
          sampleRequest.preferredImageOrientation = NativeAdImageOrientation.any
        }
      }
    }
    // This custom event uses the server parameter to carry an ad unit ID, which
    // is the most common use case.
    adLoader.delegate = self
    adLoader.adUnitID =
      adConfiguration.credentials.settings["parameter"] as? String
    self.completionHandler = completionHandler
    adLoader.fetchAd(sampleRequest)
  }
}

Objective-C

#import "SampleCustomEventNativeAd.h"

@interface SampleCustomEventNativeAd () <SampleNativeAdDelegate,
                                         GADMediationNativeAd> {
  /// The sample native ad.
  SampleNativeAd *_nativeAd;

  /// The completion handler to call when the ad loading succeeds or fails.
  GADMediationNativeLoadCompletionHandler _loadCompletionHandler;

  /// The ad event delegate to forward ad rendering events to the Google Mobile
  /// Ads SDK.
  id<GADMediationNativeAdEventDelegate> _adEventDelegate;
}
@end

- (void)loadNativeAdForAdConfiguration:
            (GADMediationNativeAdConfiguration *)adConfiguration
                     completionHandler:(GADMediationNativeLoadCompletionHandler)
                                           completionHandler {
  __block atomic_flag completionHandlerCalled = ATOMIC_FLAG_INIT;
  __block GADMediationNativeLoadCompletionHandler originalCompletionHandler =
      [completionHandler copy];

  _loadCompletionHandler = ^id<GADMediationNativeAdEventDelegate>(
      _Nullable id<GADMediationNativeAd> ad, NSError *_Nullable error) {
    // Only allow completion handler to be called once.
    if (atomic_flag_test_and_set(&completionHandlerCalled)) {
      return nil;
    }

    id<GADMediationNativeAdEventDelegate> delegate = nil;
    if (originalCompletionHandler) {
      // Call original handler and hold on to its return value.
      delegate = originalCompletionHandler(ad, error);
    }

    // Release reference to handler. Objects retained by the handler will also
    // be released.
    originalCompletionHandler = nil;

    return delegate;
  };

  SampleNativeAdLoader *adLoader = [[SampleNativeAdLoader alloc] init];
  SampleNativeAdRequest *sampleRequest = [[SampleNativeAdRequest alloc] init];

  // The Google Mobile Ads SDK requires the image assets to be downloaded
  // automatically unless the publisher specifies otherwise by using the
  // GADNativeAdImageAdLoaderOptions object's disableImageLoading property. If
  // your network doesn't have an option like this and instead only ever returns
  // URLs for images (rather than the images themselves), your adapter should
  // download image assets on behalf of the publisher. This should be done after
  // receiving the native ad object from your network's SDK, and before calling
  // the connector's adapter:didReceiveMediatedNativeAd: method.
  sampleRequest.shouldDownloadImages = YES;

  sampleRequest.preferredImageOrientation = NativeAdImageOrientationAny;
  sampleRequest.shouldRequestMultipleImages = NO;
  sampleRequest.testMode = adConfiguration.isTestRequest;

  for (GADAdLoaderOptions *loaderOptions in adConfiguration.options) {
    if ([loaderOptions isKindOfClass:[GADNativeAdImageAdLoaderOptions class]]) {
      GADNativeAdImageAdLoaderOptions *imageOptions =
          (GADNativeAdImageAdLoaderOptions *)loaderOptions;
      sampleRequest.shouldRequestMultipleImages =
          imageOptions.shouldRequestMultipleImages;

      // If the GADNativeAdImageAdLoaderOptions' disableImageLoading property is
      // YES, the adapter should send just the URLs for the images.
      sampleRequest.shouldDownloadImages = !imageOptions.disableImageLoading;
    } else if ([loaderOptions
                   isKindOfClass:[GADNativeAdMediaAdLoaderOptions class]]) {
      GADNativeAdMediaAdLoaderOptions *mediaOptions =
          (GADNativeAdMediaAdLoaderOptions *)loaderOptions;
      switch (mediaOptions.mediaAspectRatio) {
        case GADMediaAspectRatioLandscape:
          sampleRequest.preferredImageOrientation =
              NativeAdImageOrientationLandscape;
          break;
        case GADMediaAspectRatioPortrait:
          sampleRequest.preferredImageOrientation =
              NativeAdImageOrientationPortrait;
          break;
        default:
          sampleRequest.preferredImageOrientation = NativeAdImageOrientationAny;
          break;
      }
    } else if ([loaderOptions isKindOfClass:[GADNativeAdViewAdOptions class]]) {
      _nativeAdViewAdOptions = (GADNativeAdViewAdOptions *)loaderOptions;
    }
  }

  // This custom event uses the server parameter to carry an ad unit ID, which
  // is the most common use case.
  NSString *adUnit = adConfiguration.credentials.settings[@"parameter"];
  adLoader.adUnitID = adUnit;
  adLoader.delegate = self;

  [adLoader fetchAd:sampleRequest];
}

광고를 성공적으로 가져왔거나 오류가 발생하면 GADMediationNativeAdLoadCompletionHandler를 호출합니다. 성공하면 오류 매개변수의 nil 값으로 GADMediationNativeAd를 구현하는 클래스를 전달합니다. 오류가 발생하면 발생한 오류를 전달합니다.

일반적으로 이러한 메서드는 어댑터가 구현하는 서드 파티 SDK의 콜백 내에서 구현됩니다. 이 예시의 샘플 SDK에는 관련 콜백이 있는 SampleNativeAdDelegate가 있습니다.

Swift

func adLoader(
  _ adLoader: SampleNativeAdLoader, didReceive nativeAd: SampleNativeAd
) {
  extraAssets = [
    SampleCustomEventConstantsSwift.awesomenessKey: nativeAd.degreeOfAwesomeness
      ?? ""
  ]

  if let image = nativeAd.image {
    images = [GADNativeAdImage(image: image)]
  } else {
    let imageUrl = URL(fileURLWithPath: nativeAd.imageURL)
    images = [GADNativeAdImage(url: imageUrl, scale: nativeAd.imageScale)]
  }
  if let mappedIcon = nativeAd.icon {
    icon = GADNativeAdImage(image: mappedIcon)
  } else {
    let iconURL = URL(fileURLWithPath: nativeAd.iconURL)
    icon = GADNativeAdImage(url: iconURL, scale: nativeAd.iconScale)
  }

  adChoicesView = SampleAdInfoView()
  self.nativeAd = nativeAd
  if let handler = completionHandler {
    delegate = handler(self, nil)
  }
}

func adLoader(
  _ adLoader: SampleNativeAdLoader,
  didFailToLoadAdWith errorCode: SampleErrorCode
) {
  let error =
    SampleCustomEventUtilsSwift.SampleCustomEventErrorWithCodeAndDescription(
      code: SampleCustomEventErrorCodeSwift
        .SampleCustomEventErrorAdLoadFailureCallback,
      description:
        "Sample SDK returned an ad load failure callback with error code: \(errorCode)"
    )
  if let handler = completionHandler {
    delegate = handler(nil, error)
  }
}

Objective-C

- (void)adLoader:(SampleNativeAdLoader *)adLoader
    didReceiveNativeAd:(SampleNativeAd *)nativeAd {
  if (nativeAd.image) {
    _images = @[ [[GADNativeAdImage alloc] initWithImage:nativeAd.image] ];
  } else {
    NSURL *imageURL = [[NSURL alloc] initFileURLWithPath:nativeAd.imageURL];
    _images = @[ [[GADNativeAdImage alloc] initWithURL:imageURL
                                                 scale:nativeAd.imageScale] ];
  }

  if (nativeAd.icon) {
    _icon = [[GADNativeAdImage alloc] initWithImage:nativeAd.icon];
  } else {
    NSURL *iconURL = [[NSURL alloc] initFileURLWithPath:nativeAd.iconURL];
    _icon = [[GADNativeAdImage alloc] initWithURL:iconURL
                                            scale:nativeAd.iconScale];
  }

  // The sample SDK provides an AdChoices view (SampleAdInfoView). If your SDK
  // provides image and click through URLs for its AdChoices icon instead of an
  // actual UIView, the adapter is responsible for downloading the icon image
  // and creating the AdChoices icon view.
  _adChoicesView = [[SampleAdInfoView alloc] init];
  _nativeAd = nativeAd;

  _adEventDelegate = _loadCompletionHandler(self, nil);
}

- (void)adLoader:(SampleNativeAdLoader *)adLoader
    didFailToLoadAdWithErrorCode:(SampleErrorCode)errorCode {
  NSError *error = SampleCustomEventErrorWithCodeAndDescription(
      SampleCustomEventErrorAdLoadFailureCallback,
      [NSString stringWithFormat:@"Sample SDK returned an ad load failure "
                                 @"callback with error code: %@",
                                 errorCode]);
  _adEventDelegate = _loadCompletionHandler(nil, error);
}

네이티브 광고 매핑하기

SDK마다 고유한 네이티브 광고 형식이 있습니다. 예를 들어 '제목' 필드가 포함된 객체를 반환하는 SDK와 '광고 제목' 필드가 있는 SDK가 있을 수 있습니다. 또한 노출을 추적하고 클릭을 처리하는 데 사용되는 메서드가 SDK마다 다를 수 있습니다.

이러한 문제를 해결하려면 맞춤 이벤트가 미디에이션된 SDK의 네이티브 광고 객체를 수신할 때 Google 모바일 광고 SDK에서 예상하는 인터페이스와 일치하도록 SampleCustomEventNativeAd와 같은 GADMediationNativeAd를 구현하는 클래스를 사용하여 미디에이션된 SDK의 네이티브 광고 객체를 '매핑'해야 합니다.

이제 SampleCustomEventNativeAd의 구현 세부정보를 자세히 살펴보겠습니다.

매핑 저장하기

GADMediationNativeAd는 다른 SDK의 속성에서 매핑되는 특정 속성을 구현해야 합니다.

Swift

var nativeAd: SampleNativeAd?

var headline: String? {
  return nativeAd?.headline
}

var images: [GADNativeAdImage]?

var body: String? {
  return nativeAd?.body
}

var icon: GADNativeAdImage?

var callToAction: String? {
  return nativeAd?.callToAction
}

var starRating: NSDecimalNumber? {
  return nativeAd?.starRating
}

var store: String? {
  return nativeAd?.store
}

var price: String? {
  return nativeAd?.price
}

var advertiser: String? {
  return nativeAd?.advertiser
}

var extraAssets: [String: Any]? {
  return [
    SampleCustomEventConstantsSwift.awesomenessKey:
      nativeAd?.degreeOfAwesomeness
      ?? ""
  ]
}

var adChoicesView: UIView?

var mediaView: UIView? {
  return nativeAd?.mediaView
}

Objective-C

/// Used to store the ad's images. In order to implement the
/// GADMediationNativeAd protocol, we use this class to return the images
/// property.
NSArray<GADNativeAdImage *> *_images;

/// Used to store the ad's icon. In order to implement the GADMediationNativeAd
/// protocol, we use this class to return the icon property.
GADNativeAdImage *_icon;

/// Used to store the ad's ad choices view. In order to implement the
/// GADMediationNativeAd protocol, we use this class to return the adChoicesView
/// property.
UIView *_adChoicesView;

- (nullable NSString *)headline {
  return _nativeAd.headline;
}

- (nullable NSArray<GADNativeAdImage *> *)images {
  return _images;
}

- (nullable NSString *)body {
  return _nativeAd.body;
}

- (nullable GADNativeAdImage *)icon {
  return _icon;
}

- (nullable NSString *)callToAction {
  return _nativeAd.callToAction;
}

- (nullable NSDecimalNumber *)starRating {
  return _nativeAd.starRating;
}

- (nullable NSString *)store {
  return _nativeAd.store;
}

- (nullable NSString *)price {
  return _nativeAd.price;
}

- (nullable NSString *)advertiser {
  return _nativeAd.advertiser;
}

- (nullable NSDictionary<NSString *, id> *)extraAssets {
  return
      @{SampleCustomEventExtraKeyAwesomeness : _nativeAd.degreeOfAwesomeness};
}

- (nullable UIView *)adChoicesView {
  return _adChoicesView;
}

- (nullable UIView *)mediaView {
  return _nativeAd.mediaView;
}

- (BOOL)hasVideoContent {
  return self.mediaView != nil;
}

일부 미디에이션된 네트워크는 Google 모바일 광고 SDK에 정의된 것 이외의 추가 애셋을 제공할 수 있습니다. GADMediationNativeAd 프로토콜에는 Google 모바일 광고 SDK가 매퍼에서 이러한 '추가' 애셋을 검색하는 데 사용하는 extraAssets 메서드가 포함되어 있습니다.

이미지 확장 소재 매핑하기

이미지 확장 소재를 매핑하는 일은 NSString, double 등의 단순 데이터 유형을 매핑할 때보다 훨씬 더 복잡합니다. 이미지는 자동으로 다운로드될 수도 있고 URL 값으로 반환될 수도 있습니다. 픽셀 밀도 또한 달라질 수 있습니다.

이러한 세부정보를 관리할 수 있도록 Google 모바일 광고 SDK에서 GADNativeAdImage 클래스를 제공합니다. 이미지 확장 소재 정보(실제 UIImage 객체 또는 NSURL 값)는 이 클래스를 사용하여 Google 모바일 광고 SDK로 반환되어야 합니다.

매퍼 클래스에서 GADNativeAdImage를 만들어 아이콘 이미지를 저장하는 방법은 다음과 같습니다.

Swift

if let image = nativeAd.image {
  images = [GADNativeAdImage(image: image)]
} else {
  let imageUrl = URL(fileURLWithPath: nativeAd.imageURL)
  images = [GADNativeAdImage(url: imageUrl, scale: nativeAd.imageScale)]
}

Objective-C

if (nativeAd.image) {
  _images = @[ [[GADNativeAdImage alloc] initWithImage:nativeAd.image] ];
} else {
  NSURL *imageURL = [[NSURL alloc] initFileURLWithPath:nativeAd.imageURL];
  _images = @[ [[GADNativeAdImage alloc] initWithURL:imageURL
                                               scale:nativeAd.imageScale] ];
}

노출 및 클릭 이벤트

노출 또는 클릭이 발생한 경우 Google 모바일 광고 SDK와 미디에이션된 SDK 모두 알아야 하지만 하나의 SDK에서만 이러한 이벤트를 추적하면 됩니다. 맞춤 이벤트는 미디에이션된 SDK가 자체적으로 노출 및 클릭을 추적할 수 있는지 여부에 따라 두 가지 접근 방식을 사용할 수 있습니다.

Google 모바일 광고 SDK를 사용하여 클릭 및 노출 추적하기

미디에이션된 SDK에서 자체적으로 노출 및 클릭 추적을 실행하지 않지만 클릭 및 노출을 기록하는 메서드를 제공하는 경우 Google 모바일 광고 SDK에서 이러한 이벤트를 추적하여 어댑터에 알릴 수 있습니다. GADMediationNativeAd 프로토콜에는 맞춤 이벤트가 미디에이션된 네이티브 광고 객체에서 해당 메서드를 호출하기 위해 구현할 수 있는 두 가지 메서드인 didRecordImpression:didRecordClickOnAssetWithName:view:viewController:가 포함되어 있습니다.

Swift

func didRecordImpression() {
  nativeAd?.recordImpression()
}

func didRecordClickOnAsset(
  withName assetName: GADUnifiedNativeAssetIdentifier,
  view: UIView,
  wController: UIViewController
) {
  nativeAd?.handleClick(on: view)
}

Objective-C

- (void)didRecordImpression {
  if (self.nativeAd) {
    [self.nativeAd recordImpression];
  }
}

- (void)didRecordClickOnAssetWithName:(GADUnifiedNativeAssetIdentifier)assetName
                                 view:(UIView *)view
                       viewController:(UIViewController *)viewController {
  if (self.nativeAd) {
    [self.nativeAd handleClickOnView:view];
  }
}

GADMediationNativeAd 프로토콜을 구현하는 클래스는 샘플 SDK의 네이티브 광고 객체에 대한 참조를 보유하므로 해당 객체에 대해 적절한 메서드를 호출하여 클릭 또는 노출을 보고할 수 있습니다. didRecordClickOnAssetWithName:view:viewController: 메서드는 클릭이 발생한 네이티브 광고 애셋에 해당하는 View 객체라는 단일 매개변수를 사용합니다.

미디에이션된 SDK를 사용하여 클릭 및 노출 추적하기

일부 미디에이션된 SDK는 자체적으로 클릭 및 노출을 추적하려고 할 수도 있습니다. 이 경우 아래 스니펫과 같이 handlesUserClickshandlesUserImpressions 메서드를 구현해야 합니다. YES를 반환하면 맞춤 이벤트가 이러한 이벤트를 추적하는 역할을 하고 이러한 이벤트가 발생할 때 Google 모바일 광고 SDK에 알립니다.

클릭 및 노출 추적을 재정의하는 맞춤 이벤트는 didRenderInView: 메시지를 사용하여 네이티브 광고의 보기를 미디에이션된 SDK의 네이티브 광고 객체에 전달하여 미디에이션된 SDK가 실제 추적을 수행할 수 있도록 합니다. 이 가이드의 코드 스니펫을 발췌한 맞춤 이벤트 예시 프로젝트의 샘플 SDK는 이 방식을 사용하지 않지만, 이 방식을 사용한다면 맞춤 이벤트 코드에서 아래 스니펫과 같이 setNativeAdView:view: 메서드를 호출합니다.

Swift

func handlesUserClicks() -> Bool {
  return true
}
func handlesUserImpressions() -> Bool {
  return true
}

func didRender(
  in view: UIView, clickableAssetViews: [GADNativeAssetIdentifier: UIView],
  nonclickableAssetViews: [GADNativeAssetIdentifier: UIView],
  viewController: UIViewController
) {
  // This method is called when the native ad view is rendered. Here you would pass the UIView
  // back to the mediated network's SDK.
  self.nativeAd?.setNativeAdView(view)
}

Objective-C

- (BOOL)handlesUserClicks {
  return YES;
}

- (BOOL)handlesUserImpressions {
  return YES;
}

- (void)didRenderInView:(UIView *)view
       clickableAssetViews:(NSDictionary<GADNativeAssetIdentifier, UIView *> *)
                               clickableAssetViews
    nonclickableAssetViews:(NSDictionary<GADNativeAssetIdentifier, UIView *> *)
                               nonclickableAssetViews
            viewController:(UIViewController *)viewController {
  // This method is called when the native ad view is rendered. Here you would
  // pass the UIView back to the mediated network's SDK. Playing video using
  // SampleNativeAd's playVideo method
  [_nativeAd setNativeAdView:view];
}

Google 모바일 광고 SDK에 미디에이션 이벤트 전달하기

로드된 광고로 GADMediationNativeLoadCompletionHandler를 호출한 후 반환된 GADMediationNativeAdEventDelegate 대리자 객체를 어댑터에서 사용하여 프레젠테이션 이벤트를 서드 파티 SDK에서 Google 모바일 광고 SDK로 전달할 수 있습니다.

맞춤 이벤트가 이러한 콜백을 최대한 많이 전달하여 앱이 Google 모바일 광고 SDK에서 동등한 이벤트를 수신하도록 하는 것이 중요합니다. 다음은 콜백을 사용하는 예입니다.

네이티브 광고용 맞춤 이벤트 구현이 완료되었습니다. 전체 예시는 GitHub에서 확인할 수 있습니다.