본문으로 건너뛰기

Capacitor에서 AdMob GDPR 동의 처리하기

Handle AdMob GDPR consent in a Capacitor app with Google's UMP SDK: consent form, privacy options, ATT order, debug geography and error handling, with code.

Article Credits

마틴 도나디유

Writer

발레리아

리뷰어

조던

Editor

Capacitor 앱에서 AdMob GDPR 동의를 처리하는 방법

Capacitor 앱에서 AdMob GDPR 동의를 처리하려면 Google의 사용자 메시징 플랫폼 (UMP) SDK: 런치 시마다 동의 정보를 요청하고, 필요할 경우 동의 양식을 표시하고, canRequestAds true이면 광고를 초기화합니다. 또한 사용자가 선택을 변경할 수 있는 개인 정보 옵션을 제공해야 하며, iOS에서는 UMP 흐름이 앱 추적 투명성 프롬프트 전에 와야 합니다. 이 안내서에서는 두 가지 설정을 보여줍니다: 자바스크립트에서 UMP를.wrap하는 플러그인 및 작은 네이티브 동의 브리지를 사용하는 것. @capgo/capacitor-admob __CAPGO_KEEP_1__

UMP는 무엇을 하며 AdMob이 이를 요구하는 이유는 무엇인가?

Since January 2024, Google requires publishers that serve ads to users in the EEA, the UK and Switzerland to use a Google-certified consent management platform (CMP) integrated with the IAB Transparency and Consent Framework (TCF). The UMP SDK is Google’s own certified CMP, free and built into the AdMob console.

UMP는 당신에게 세 가지 일을 합니다:

  1. 사용자가 동의가 필요한 지역인지 감지합니다.
  2. 사용자가 선택한 언어로 구성된 AdMob의 동의 메시지를 표시합니다.
  3. 장치에 사용자의 선택을 TC 문자열로 저장합니다 (표준) IABTCF_ 키들 UserDefaults iOS와 SharedPreferences 안드로이드에서 사용하는 Google 모바일 광고 SDK와 광고 매개변수 파트너가 광고 요청 시 읽는 내용입니다.

go to

create the messages in AdMob

AdMob 콘솔에서 이동하여 개인 정보 보호 및 통지:

  • 생성 유럽 규정 GDPR 통지 (message) 생성. 선택한 앱에 적용할 언어 및 광고 파트너 목록을 지정합니다.
  • 선택 사항: 미국 주별 통지 생성.
  • iOS에서 IDFA 설명서 통지를 생성하여 앱 추적성 투명성 요청 시 표시합니다. UMP는 시스템 ATT 프롬프트 전에 표시합니다. 통지를 공개합니다. 공개되지 않은 통지는 표시되지 않습니다.
  • 폼이 앱에 나중에 나타나지 않으면, 공개되지 않은 또는 할당되지 않은 통지는 첫 번째로 확인해야 합니다.

앱에서 이 폼이 나중에 나타나지 않는다면, 공개되지 않은 메시지 또는 할당되지 않은 메시지가 첫 번째로 확인해야 합니다.

Step 2: operations 순서 결정

대부분의 앱에 적합한 순서:

  1. 앱이 시작됩니다.
  2. UMP를 사용하여 콘센트 정보 업데이트 (매번 런칭).
  3. 필요한 경우, 동의 양식을 로드 및 표시.
  4. 만약 canRequestAds true라면, 모바일 광고 SDK를 시작하고 광고를 로드합니다.
  5. UMP가 개인 정보 설정이 필요하다고 말할 때, 설정 화면에 '개인 정보 설정' 항목을 표시합니다.

Google은 이전 세션에서 true가 이미 true였던 경우, 동의 요청과 동시에 모바일 광고 SDK를 시작할 수 있습니다. 이는 반복 사용자에게 첫 번째 광고까지의 시간을 단축합니다. canRequestAds Option A: JavaScript에서 UMP를.wrap하는 플러그인

(버전 8은 __CAPGO_KEEP_0__ 8을 지원합니다.) UMP 메소드를 직접 노출합니다:

@capacitor-community/admob (version 8은 Capacitor 8을 지원) UMP 메소드를 직접 노출합니다. requestConsentInfo, showConsentForm, showPrivacyOptionsForm 그리고 resetConsentInfo.

bun add @capacitor-community/admob
bunx cap sync
import {
  AdMob,
  AdmobConsentStatus,
  AdmobConsentDebugGeography,
} from '@capacitor-community/admob';

export async function initAdsWithConsent(isDebug: boolean) {
  await AdMob.initialize();

  let info = await AdMob.requestConsentInfo(
    isDebug
      ? {
          debugGeography: AdmobConsentDebugGeography.EEA,
          testDeviceIdentifiers: ['YOUR_TEST_DEVICE_ID'],
        }
      : undefined,
  );

  if (info.isConsentFormAvailable && info.status === AdmobConsentStatus.REQUIRED) {
    info = await AdMob.showConsentForm();
  }

  // The enum value is the string 'REQUIRED'
  const showPrivacyEntry = String(info.privacyOptionsRequirementStatus) === 'REQUIRED';

  return { canRequestAds: info.canRequestAds, showPrivacyEntry };
}

export async function openPrivacyOptions() {
  await AdMob.showPrivacyOptionsForm();
}

이 플러그인은 위의 순서에서 유일한 예외입니다: README는 AdMob.initialize() 전에 requestConsentInfo()iOS에서 동의서를 표시할 수 없으므로 initialize() 실행되기 전에 initialize() SDK를 시작하지만 광고를 요청하지 않으므로 중요하는 규칙은 광고를 로드하지 않는 것이다. canRequestAds 이 SDK을 시작하지만 광고를 요청하지 않으므로, 중요한 규칙은 광고를 로드하지 않는 것입니다. AdMob.start().

Check canRequestAds 체크

Option B: @capgo/capacitor-admob native UMP 연결 브릿지

@capgo/capacitor-admob Capacitor는 вам을 AdMob.start()이것은 AdMob.start().

bun add @capgo/capacitor-admob
bunx cap sync

UMP SDK 추가

  • iOS: Swift Package Manager를 사용하여 Google의 패키지를 추가하거나, 프로젝트가 CocoaPods를 사용하는 경우 CocoaPods의 pod과 동일한 이름의 pod을 추가합니다. GoogleUserMessagingPlatform package with Swift Package Manager (Capacitor 8 projects use SPM by default), or the CocoaPods pod of the same name if your project still uses CocoaPods.
  • in in android/app/build.gradle:
dependencies {
    implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}

iOS 동의 플러그인

Create ios/App/App/ConsentPlugin.swift:

import Capacitor
import UserMessagingPlatform

@objc(ConsentPlugin)
public class ConsentPlugin: CAPPlugin, CAPBridgedPlugin {
    public let identifier = "ConsentPlugin"
    public let jsName = "Consent"
    public let pluginMethods: [CAPPluginMethod] = [
        CAPPluginMethod(name: "gather", returnType: CAPPluginReturnPromise),
        CAPPluginMethod(name: "showPrivacyOptions", returnType: CAPPluginReturnPromise),
    ]

    @objc func gather(_ call: CAPPluginCall) {
        let parameters = RequestParameters()
        DispatchQueue.main.async {
            ConsentInformation.shared.requestConsentInfoUpdate(with: parameters) { error in
                if let error = error {
                    call.reject(error.localizedDescription)
                    return
                }
                guard let vc = self.bridge?.viewController else {
                    call.reject("No view controller")
                    return
                }
                ConsentForm.loadAndPresentIfRequired(from: vc) { formError in
                    if let formError = formError {
                        call.reject(formError.localizedDescription)
                        return
                    }
                    call.resolve(self.state())
                }
            }
        }
    }

    @objc func showPrivacyOptions(_ call: CAPPluginCall) {
        DispatchQueue.main.async {
            guard let vc = self.bridge?.viewController else {
                call.reject("No view controller")
                return
            }
            ConsentForm.presentPrivacyOptionsForm(from: vc) { formError in
                if let formError = formError {
                    call.reject(formError.localizedDescription)
                    return
                }
                call.resolve(self.state())
            }
        }
    }

    private func state() -> [String: Any] {
        return [
            "canRequestAds": ConsentInformation.shared.canRequestAds,
            "privacyOptionsRequired":
                ConsentInformation.shared.privacyOptionsRequirementStatus == .required,
        ]
    }
}

Android 동의 플러그인 CAPBridgeViewController UMP __CAPGO_KEEP_0__ 추가 Main.storyboard:

import Capacitor

class MainViewController: CAPBridgeViewController {
    override open func capacitorDidLoad() {
        bridge?.registerPluginInstance(ConsentPlugin())
    }
}

만들기 android/app/src/main/java/com/example/app/ConsentPlugin.java (사용자 패키지 이름을 사용하세요):

package com.example.app;

import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.google.android.ump.ConsentInformation;
import com.google.android.ump.ConsentRequestParameters;
import com.google.android.ump.UserMessagingPlatform;

@CapacitorPlugin(name = "Consent")
public class ConsentPlugin extends Plugin {

    @PluginMethod
    public void gather(PluginCall call) {
        ConsentInformation info = UserMessagingPlatform.getConsentInformation(getContext());
        ConsentRequestParameters params = new ConsentRequestParameters.Builder().build();

        getActivity().runOnUiThread(() ->
            info.requestConsentInfoUpdate(
                getActivity(),
                params,
                () -> UserMessagingPlatform.loadAndShowConsentFormIfRequired(getActivity(), formError -> {
                    if (formError != null) {
                        call.reject(formError.getMessage());
                        return;
                    }
                    call.resolve(state(info));
                }),
                requestError -> call.reject(requestError.getMessage())
            )
        );
    }

    @PluginMethod
    public void showPrivacyOptions(PluginCall call) {
        getActivity().runOnUiThread(() ->
            UserMessagingPlatform.showPrivacyOptionsForm(getActivity(), formError -> {
                if (formError != null) {
                    call.reject(formError.getMessage());
                    return;
                }
                call.resolve(state(UserMessagingPlatform.getConsentInformation(getContext())));
            })
        );
    }

    private JSObject state(ConsentInformation info) {
        JSObject ret = new JSObject();
        ret.put("canRequestAds", info.canRequestAds());
        ret.put(
            "privacyOptionsRequired",
            info.getPrivacyOptionsRequirementStatus() ==
                ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
        );
        return ret;
    }
}

등록하세요 MainActivity.java 이전 super.onCreate:

public class MainActivity extends BridgeActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        registerPlugin(ConsentPlugin.class);
        super.onCreate(savedInstanceState);
    }
}

TypeScript에서 호출하세요

import { registerPlugin } from '@capacitor/core';
import { AdMob, BannerAd } from '@capgo/capacitor-admob';

interface ConsentState {
  canRequestAds: boolean;
  privacyOptionsRequired: boolean;
}

interface ConsentPlugin {
  gather(): Promise<ConsentState>;
  showPrivacyOptions(): Promise<ConsentState>;
}

const Consent = registerPlugin<ConsentPlugin>('Consent');

export async function startAds() {
  let state: ConsentState;
  try {
    state = await Consent.gather();
  } catch (err) {
    console.warn('Consent flow failed', err);
    return { showPrivacyEntry: false };
  }

  if (state.canRequestAds) {
    await AdMob.start();
    const banner = new BannerAd({
      adUnitId: 'ca-app-pub-3940256099942544/2934735716', // Google test banner (iOS)
      position: 'bottom',
    });
    await banner.show();
  }

  return { showPrivacyEntry: state.privacyOptionsRequired };
}

export const openPrivacyOptions = () => Consent.showPrivacyOptions();

자연스러운 code 변경은 새로운 스토어 빌드를 필요로 합니다. 그 후, 설정 화면의 동의 복사본과 광고 배치 논리가 code 라이브 업데이트와 함께 배포될 수 있습니다. Capgo 라이브 업데이트.

3단계: 개인 정보 옵션 엔트리 포인트 추가

UMP가 개인 정보 옵션이 필요하다고 보고할 때, Google은 사용자가 동의 선택지를 다시 열 수 있는 방법을 제공해야 합니다. 설정 화면에 '개인 정보 설정' 항목을 추가하고 필요하지 않으면 숨기세요:

const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());

사용자가 선택을 변경한 후, 다시 읽어야 합니다. canRequestAds 다음 광고 요청 전에 다시 읽어야 합니다.

4단계: iOS의 앱 추적성 투명성

ATT는 GDPR와 별도로 작동합니다. ATT는 iOS에서 IDFA에 대한 접근을 제어합니다. GDPR 동의는 개인 데이터가 처리될 수 있는지 여부와 처리 방법을 제어합니다. 일반적으로 iOS에서 EU 사용자는 두 가지 모두 필요합니다.

  • 추가 NSUserTrackingUsageDescription 으로 Info.plist.
  • UMP는 IDFA 설명 메시지를 표시하고 ATT를 트리거하도록 구성했을 때만 ATT를 표시하고 트리거합니다. 그렇지 않으면 동의 흐름 후 ATT를 직접 요청하세요.

그것과 함께 @capgo/capacitor-admob 당신은 ATT 상태를 직접 읽고 요청할 수 있습니다:

import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';

const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
  await AdMob.requestTrackingAuthorization();
}

Capgo도 다른 SDK에 대해 ATT가 필요할 때 ATT를 위한 별도의 앱 추적 투명성 플러그인을 가지고 있습니다. 추적을 선언하지 않으면 개인 정보 매니페스트와 앱 스토어 개인 정보 레이블에서 오류가 발생합니다. 자세한 내용은 개인 정보 매니페스트 가이드를 참조하세요. 5단계: 디버그 지리학으로 테스트 UMP는 EEA, UK 또는 스위스에서만 GDPR 양식을 사용자에게 표시합니다. 다른 지역에서 테스트하려면: ATT는 GDPR와 별도로 작동합니다. ATT는 iOS에서 IDFA에 대한 접근을 제어합니다. GDPR 동의는 개인 데이터가 처리될 수 있는지 여부와 처리 방법을 제어합니다. 일반적으로 iOS에서 EU 사용자는 두 가지 모두 필요합니다..

5단계: 디버그 지리학으로 테스트

UMP는 EEA, UK 또는 스위스에서만 GDPR 양식을 사용자에게 표시합니다. 다른 지역에서 테스트하려면:

  • 앱을 한 번 실행하고 UMP에서 장치의 해시된 ID와 관련된 로그 라인을 찾으세요.
  • 테스트 장치로 추가하고 디버그 지구를 EEA로 설정하세요. 커뮤니티 플러그인과 함께 debugGeography 그리고 testDeviceIdentifiers 로 requestConsentInfo. 네이티브 브리지에서 DebugSettings iOS에서 ConsentDebugSettings Android에서 디버그 빌드에서만.
  • 실행 중인 경우 다시 동의를 초기화하세요 (resetConsentInfo() 커뮤니티 플러그인에서 ConsentInformation.reset() 네이티브에서) 이므로 폼이 다시 나타납니다.

디버그 설정을 릴리스 빌드에 배포하지 마세요. 빌드 플래그에 게이트를 걸어주세요.

6단계: 앱을 블록하지 않고 오류를 처리하세요

네트워크 오류, 앱 ID 설정 오류 또는 게시되지 않은 메시지로 인해 동의 요청이 실패할 수 있습니다. 실패를 '현재 광고가 없어요'로 대처하고, 앱이 충돌하지 않도록 하세요.

  • 만약 requestConsentInfoUpdate 실패했지만 canRequestAds 이전 세션에서 true로 설정된 경우 여전히 광고 요청이 가능합니다.
  • 폼이 로드되지 않으면 로그를 남기고 다음 런칭 시 다시 시도하세요.
  • 동의 폼이 화면에 나타나면 광고를 표시하지 마세요.

문제 해결

컨텍스트: 지원 / 프리미엄 지원 페이지 또는 푸터 지원 섹션. 역할: 섹션 또는 페이지 제목. 보이는 곳: 페이지 support-policy.astro. 메시지 키 `support_policy_troubleshooting_title` (지원 정책 문제 해결 제목). 폼이 절대 나타나지 않습니다. Info.plist and AndroidManifest.xml 컨텍스트: Capgo 마케팅 웹사이트. 역할: 짧은 UI 레이블 또는 네비게이션 아이템. 보이는 곳: 페이지 trust.astro. 메시지 키 `and` (그리고).

콘솔과 일치합니다. (콘솔과 일치합니다.) 사용자가 선택 없이 닫았거나, 혹은 프로덕션에서 reset을 호출했습니다 code.

canRequestAds 미국 사용자에게는 false입니다. SDK을 통해만 제한된 광고만 제공하고, 이를 우회하지 마십시오. 사용자가 판매 또는 공유를 거부했습니다.

미디어션 파트너는 동의를 받지 못했습니다. 각 파트너 어댑터가 최신 상태인지 확인하고 GDPR 메시지의 광고 파트너 목록에 포함되어 있는지 확인하십시오.

iOS ATT 프롬프트는 절대 나타나지 않습니다. NSUserTrackingUsageDescription 동의가 누락되거나, 사용자가 설정에서 '앱이 트래킹을 요청할 수 있도록 허용'을 비활성화했습니다.

형식 외의 준수

UMP는 광고 쪽을 처리합니다. 앱은 여전히 AdMob, 데이터 보유 기간, 데이터 요청 처리 방법을 포함한 개인 정보 보호 정책이 필요합니다. 우리의 GDPR 준수 체크리스트 앱의 나머지 부분을 다룹니다.

실시간 업데이트 Capacitor 앱

웹-layer 버그가 실시간으로 활성화되면 Capgo을 통해 픽스를 배포할 수 있습니다. 앱 스토어 승인 대기 없이 사용자가 배경에서 업데이트를 받을 수 있습니다. native 변경 사항은 일반적인 검토 경로에 남아 있습니다.

최신 블로그

Capgo은 전문적인 모바일 앱을 만들기 위해 필요한 최고의 통찰력을 제공합니다.