시작하기

Google EU 사용자 동의 정책에 따라 영국과 함께 유럽 경제 지역 (EEA)의 사용자에게 특정 정보를 공개하고, 법적으로 필요한 경우 쿠키 또는 기타 로컬 저장소를 사용하고 개인 정보 (예: AdID)를 사용하여 광고를 게재한다는 것에 대한 사용자의 동의를 얻어야 합니다. 이 정책에는 EU 온라인 개인 정보 보호 지침 및 개인 정보 보호법 (GDPR)의 요구사항이 반영되어 있습니다.

게시자가 이 정책에 따른 의무사항을 준수하도록 지원하기 위해 Google에서는 사용자 메시지 플랫폼 (UMP) SDK를 제공합니다. UMP SDK는 최신 IAB 표준을 지원하도록 업데이트되었습니다. 이제 개인 정보 보호 및 메시지에서 AdMob 이러한 모든 구성을 편리하게 처리할 수 있습니다.

기본 요건

  • Android API 수준 21 이상 (Android의 경우)

메시지 유형 만들기

AdMob 계정의 개인 정보 보호 및 메시지 탭에서 사용 가능한 사용자 메시지 유형 중 하나로 사용자 메시지를 만듭니다. UMP SDK는 프로젝트에 설정된 AdMob 애플리케이션 ID에서 생성된 사용자 메시지를 표시하려고 시도합니다. 애플리케이션에 구성된 메시지가 없으면 SDK가 오류를 반환합니다.

자세한 내용은 개인 정보 보호 및 메시지에 대한 정보를 참고하세요.

SDK 설치

  1. 단계에 따라 Google 모바일 광고 (GMA) C++ SDK를 설치합니다. UMP C++ SDK는 GMA C++ SDK에 포함되어 있습니다.

  2. 계속하기 전에 프로젝트에서 앱의 AdMob 앱 ID를 구성해야 합니다.

  3. 코드에서 ConsentInfo::GetInstance()를 호출하여 UMP SDK를 초기화합니다.

    • Android에서는 NDK에서 제공하는 JNIEnvActivity를 전달해야 합니다. 이 작업은 GetInstance()를 처음 호출할 때만 필요합니다.
    • 또는 앱에서 이미 Firebase C++ SDK를 사용 중인 경우 GetInstance()를 처음 호출할 때 firebase::App를 전달할 수 있습니다.
    #include "firebase/gma/ump.h"
    
    namespace ump = ::firebase::gma::ump;
    
    // Initialize using a firebase::App
    void InitializeUserMessagingPlatform(const firebase::App& app) {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(app);
    }
    
    // Initialize without a firebase::App
    #ifdef ANDROID
    void InitializeUserMessagingPlatform(JNIEnv* jni_env, jobject activity) {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(jni_env, activity);
    }
    #else  // non-Android
    void InitializeUserMessagingPlatform() {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
    }
    #endif
    

후속 ConsentInfo::GetInstance() 호출은 모두 동일한 인스턴스를 반환합니다.

UMP SDK 사용을 마쳤으면 ConsentInfo 인스턴스를 삭제하여 SDK를 종료할 수 있습니다.

void ShutdownUserMessagingPlatform() {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
  delete consent_info;
}

Future를 사용하여 비동기 작업 모니터링

firebase::Future를 사용하면 비동기 메서드 호출의 완료 상태를 확인할 수 있습니다.

비동기식으로 작동하는 모든 UMP C++ 함수와 메서드 호출은 Future를 반환하고, 최근 작업에서 Future를 검색하는 '최종 결과' 함수도 제공합니다.

Future에서 결과를 가져오는 방법에는 두 가지가 있습니다.

  1. OnCompletion()를 호출하여 작업 완료 시 호출되는 자체 콜백 함수를 전달합니다.
  2. Futurestatus()를 주기적으로 확인하세요. 상태kFutureStatusPending에서 kFutureStatusCompleted로 변경되면 작업이 완료된 것입니다.

비동기 작업이 완료된 후에는 Futureerror()에서 작업의 오류 코드를 가져와야 합니다. 오류 코드가 0 (kConsentRequestSuccess 또는 kConsentFormSuccess)이면 작업이 성공적으로 완료된 것입니다. 그렇지 않은 경우 오류 코드와 error_message()를 확인하여 무엇이 잘못되었는지 파악합니다.

완료 콜백

다음은 OnCompletion를 사용하여 비동기 작업이 완료될 때 호출되는 완료 콜백을 설정하는 방법을 보여주는 예입니다.

void MyApplicationStart() {
  // [... other app initialization code ...]

  ump::ConsentInfo *consent_info = ump::ConsentInfo::GetInstance();

  // See the section below for more information about RequestConsentInfoUpdate.
  firebase::Future<void> result = consent_info->RequestConsentInfoUpdate(...);

  result.OnCompletion([](const firebase::Future<void>& req_result) {
    if (req_result.error() == ump::kConsentRequestSuccess) {
      // Operation succeeded. You can now call LoadAndShowConsentFormIfRequired().
    } else {
      // Operation failed. Check req_result.error_message() for more information.
    }
  });
}

루프 폴링 업데이트

이 예에서는 앱 실행 시 비동기 작업이 시작된 후 게임의 업데이트 루프 함수 (프레임당 한 번 실행)의 다른 위치에서 결과를 확인합니다.

ump::ConsentInfo *g_consent_info = nullptr;
bool g_waiting_for_request = false;

void MyApplicationStart() {
  // [... other app initialization code ...]

  g_consent_info = ump::ConsentInfo::GetInstance();
  // See the section below for more information about RequestConsentInfoUpdate.
  g_consent_info->RequestConsentInfoUpdate(...);
  g_waiting_for_request = true;
}

// Elsewhere, in the game's update loop, which runs once per frame:
void MyGameUpdateLoop() {
  // [... other game logic here ...]

  if (g_waiting_for_request) {
    // Check whether RequestConsentInfoUpdate() has finished.
    // Calling "LastResult" returns the Future for the most recent operation.
    firebase::Future<void> result =
      g_consent_info->RequestConsentInfoUpdateLastResult();

    if (result.status() == firebase::kFutureStatusComplete) {
      g_waiting_for_request = false;
      if (result.error() == ump::kConsentRequestSuccess) {
        // Operation succeeded. You can call LoadAndShowConsentFormIfRequired().
      } else {
        // Operation failed. Check result.error_message() for more information.
      }
    }
  }
}

firebase::Future에 관한 자세한 내용은 Firebase C++ SDK 문서GMA C++ SDK 문서를 참고하세요.

앱을 실행할 때마다 RequestConsentInfoUpdate()를 사용하여 사용자 동의 정보 업데이트를 요청해야 합니다. 이를 통해 사용자가 아직 동의를 제공하지 않았는지 또는 동의가 만료되었는지 여부를 판단합니다.

#include "firebase/gma/ump.h"

namespace ump = ::firebase::gma::ump;

void MyApplicationStart() {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();

  // Create a ConsentRequestParameters struct.
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age
  // of consent.
  params.tag_for_under_age_of_consent = false;

  consent_info->RequestConsentInfoUpdate(params).OnCompletion(
    [](const Future<void>& result) {
      if (result.error() != ump::kConsentRequestSuccess) {
        LogMessage("Error requesting consent update: %s", result.error_message());
      } else {
        // Consent status is now available.
      }
    });
}

완료 콜백이 아닌 업데이트 루프 폴링을 사용하여 완료를 확인하는 예는 를 참고하세요.

필요한 경우 동의 양식 로드 및 표시

최신 동의 상태를 수신했으면 ConsentInfo 클래스에서 LoadAndShowConsentFormIfRequired() 를 호출하여 동의 양식을 로드하세요. 동의 상태가 필요한 경우 SDK는 양식을 로드한 후 제공된 FormParent에서 양식을 즉시 표시합니다. 양식을 닫으면 Future 완료 호출됩니다. 동의가 필요하지 않으면 Future 완료 가 즉시 호출됩니다.

void MyApplicationStart(ump::FormParent parent) {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();

  // Create a ConsentRequestParameters struct..
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age of consent.
  params.tag_for_under_age_of_consent = false;

  consent_info->RequestConsentInfoUpdate(params).OnCompletion(
    [*](const Future<void>& req_result) {
      if (req_result.error() != ump::kConsentRequestSuccess) {
        // req_result.error() is a kConsentRequestError enum.
        LogMessage("Error requesting consent update: %s", req_result.error_message());
      } else {
        consent_info->LoadAndShowConsentFormIfRequired(parent).OnCompletion(
        [*](const Future<void>& form_result) {
          if (form_result.error() != ump::kConsentFormSuccess) {
            // form_result.error() is a kConsentFormError enum.
            LogMessage("Error showing consent form: %s", form_result.error_message());
          } else {
            // Either the form was shown and completed by the user, or consent was not required.
          }
        });
      }
    });
}

사용자가 선택을 하거나 양식을 닫은 후에 작업을 실행해야 하는 경우 LoadAndShowConsentFormIfRequired()에서 반환한 Future를 처리하는 코드에 로직을 배치합니다.

광고 요청

앱에서 광고를 요청하기 전에 ConsentInfo::GetInstance()‑>CanRequestAds()를 사용하여 사용자의 동의를 얻었는지 확인하세요. 동의를 수집할 때 다음 두 곳에서 확인할 수 있습니다.

  1. 현재 세션에서 동의를 수집한 후
  2. RequestConsentInfoUpdate()를 호출한 직후 이전 세션에서 동의를 받았을 수 있습니다. 지연 시간 권장사항에 따라 앱 실행 후 최대한 빨리 광고 로드를 시작할 수 있도록 콜백이 완료될 때까지 기다리지 않는 것이 좋습니다.

동의 수집 프로세스 중에 오류가 발생해도 광고를 요청해야 합니다. UMP SDK는 이전 세션의 동의 상태를 사용합니다.

다음 전체 예에서는 업데이트 루프 폴링을 사용하지만 OnCompletion 콜백을 사용하여 비동기 작업을 모니터링할 수도 있습니다. 코드 구조에 더 적합한 기법을 사용하세요.

#include "firebase/future.h"
#include "firebase/gma/gma.h"
#include "firebase/gma/ump.h"

namespace gma = ::firebase::gma;
namespace ump = ::firebase::gma::ump;
using firebase::Future;

ump::ConsentInfo* g_consent_info = nullptr;
// State variable for tracking the UMP consent flow.
enum { kStart, kRequest, kLoadAndShow, kInitGma, kFinished, kErrorState } g_state = kStart;
bool g_ads_allowed = false;

void MyApplicationStart() {
  g_consent_info = ump::ConsentInfo::GetInstance(...);

  // Create a ConsentRequestParameters struct..
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age of consent.
  params.tag_for_under_age_of_consent = false;

  g_consent_info->RequestConsentInfoUpdate(params);
  // CanRequestAds() can return a cached value from a previous run immediately.
  g_ads_allowed = g_consent_info->CanRequestAds();
  g_state = kRequest;
}

// This function runs once per frame.
void MyGameUpdateLoop() {
  // [... other game logic here ...]

  if (g_state == kRequest) {
    Future<void> req_result = g_consent_info->RequestConsentInfoUpdateLastResult();

    if (req_result.status() == firebase::kFutureStatusComplete) {
      g_ads_allowed = g_consent_info->CanRequestAds();
      if (req_result.error() == ump::kConsentRequestSuccess) {
        // You must provide the FormParent (Android Activity or iOS UIViewController).
        ump::FormParent parent = GetMyFormParent();
        g_consent_info->LoadAndShowConsentFormIfRequired(parent);
        g_state = kLoadAndShow;
      } else {
        LogMessage("Error requesting consent status: %s", req_result.error_message());
        g_state = kErrorState;
      }
    }
  }
  if (g_state == kLoadAndShow) {
    Future<void> form_result = g_consent_info->LoadAndShowConsentFormIfRequiredLastResult();

    if (form_result.status() == firebase::kFutureStatusComplete) {
      g_ads_allowed = g_consent_info->CanRequestAds();
      if (form_result.error() == ump::kConsentRequestSuccess) {
        if (g_ads_allowed) {
          // Initialize GMA. This is another asynchronous operation.
          firebase::gma::Initialize();
          g_state = kInitGma;
        } else {
          g_state = kFinished;
        }
        // Optional: shut down the UMP SDK to save memory.
        delete g_consent_info;
        g_consent_info = nullptr;
      } else {
        LogMessage("Error displaying consent form: %s", form_result.error_message());
        g_state = kErrorState;
      }
    }
  }
  if (g_state == kInitGma && g_ads_allowed) {
    Future<gma::AdapterInitializationStatus> gma_future = gma::InitializeLastResult();

    if (gma_future.status() == firebase::kFutureStatusComplete) {
      if (gma_future.error() == gma::kAdErrorCodeNone) {
        g_state = kFinished;
        // TODO: Request an ad.
      } else {
        LogMessage("Error initializing GMA: %s", gma_future.error_message());
        g_state = kErrorState;
      }
    }
  }
}

공개 설정 옵션

일부 동의 양식의 경우 사용자가 언제든지 동의를 수정해야 합니다. 필요한 경우 다음 단계를 따라 개인 정보 보호 옵션 버튼을 구현합니다.

이 경우 방법은 다음과 같습니다.

  1. 개인 정보 보호 옵션 양식을 트리거할 수 있는 UI 요소(예: 앱의 설정 페이지에 있는 버튼)를 구현합니다.
  2. LoadAndShowConsentFormIfRequired() 가 완료되면getPrivacyOptionsRequirementStatus() 를 확인하여 개인 정보 보호 옵션 양식을 표시할 수 있는 UI 요소를 표시할지 결정합니다.
  3. 사용자가 UI 요소와 상호작용하면showPrivacyOptionsForm() 를 호출하여 양식을 표시합니다. 그러면 사용자가 언제든지 개인 정보 보호 옵션을 업데이트할 수 있습니다.

테스트

개발 중에 앱에서 통합을 테스트하려면 아래 단계에 따라 프로그래매틱 방식으로 테스트 기기를 등록하세요.

  1. RequestConsentInfoUpdate()를 호출합니다.
  2. 로그 출력에서 다음과 같은 메시지를 확인합니다. 메시지에는 기기 ID와 이를 테스트 기기로 추가하는 방법이 나와 있습니다.

    Android

    Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("33BE2250B43518CCDA7DE426D04EE231") to set this as a debug device.

    iOS

    <UMP SDK>To enable debug mode for this device, set: UMPDebugSettings.testDeviceIdentifiers = @[2077ef9a63d2b398840261c8221a0c9b]
  3. 테스트 기기 ID를 클립보드에 복사합니다.

  4. 코드를 수정하여 set ConsentRequestParameters.debug_settings.debug_device_ids to 합니다.

void MyApplicationStart() {
  ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);

  ump::ConsentRequestParameters params;
  params.tag_for_under_age_of_consent = false;
  params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};

  consent_info->RequestConsentInfoUpdate(params);
}

지역 강제 설정

UMP SDK는 ConsentRequestParameters.debug_settings.debug_geography를 사용하여 기기가 EEA 또는 영국에 있는 것처럼 앱 동작을 테스트할 수 있는 방법을 제공합니다. 디버그 설정은 테스트 기기에서만 작동합니다.

void MyApplicationStart() {
  ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);

  ump::ConsentRequestParameters params;
  params.tag_for_under_age_of_consent = false;
  params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};
  // Geography appears as EEA for debug devices.
  params.debug_settings.debug_geography = ump::kConsentDebugGeographyEEA

  consent_info->RequestConsentInfoUpdate(params);
}

UMP SDK로 앱을 테스트할 때 사용자의 첫 설치 경험을 시뮬레이션할 수 있도록 SDK의 상태를 재설정하는 것이 유용할 수 있습니다. SDK에서는 이를 위한 Reset() 메서드를 제공합니다.

  ConsentInfo::GetInstance()->Reset();