시작하기

Google 사용자 메시지 플랫폼 (UMP) SDK는 개인 정보 보호 옵션을 관리하는 데 도움이 되는 개인 정보 보호 및 메시지 도구입니다. 자세한 내용은 개인 정보 보호 및 메시지에 대한 정보를 참고하세요.

기본 요건

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

메시지 유형 만들기

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

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

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::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());
      }
      // Consent information is successfully updated.
    });
}

개인 정보 보호 메시지 양식 로드 및 표시

최신 동의 상태를 수신한 후 LoadAndShowConsentFormIfRequired()를 호출하여 사용자 동의를 수집하는 데 필요한 양식을 로드합니다. 로드 후 양식이 즉시 표시됩니다.

#include "firebase/gma/ump.h"

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

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 privacy message 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()를 사용하여 사용자의 동의를 얻었는지 확인합니다.

동의를 수집하는 동안 광고를 요청할 수 있는지 확인할 수 있는 위치는 다음과 같습니다.

  • UMP SDK가 현재 세션에서 동의를 수집한 후
  • RequestConsentInfoUpdate()를 호출한 직후 UMP SDK가 이전 앱 세션에서 동의를 얻었을 수 있습니다.

동의 수집 과정에서 오류가 발생하면 광고를 요청할 수 있는지 확인하세요. 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 privacy message 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;
      }
    }
  }
}

테스트

개발하면서 앱의 통합을 테스트하려면 이 단계에 따라 테스트 기기를 프로그래매틱 방식으로 등록하세요. 앱을 출시하기 전에 이러한 테스트 기기 ID를 설정하는 코드를 반드시 삭제하세요.

  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. ConsentRequestParameters.debug_settings.debug_device_ids를 테스트 기기 ID 목록으로 설정하도록 코드를 수정합니다.

    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는 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();