블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2797)
Unity3D (853)
Script (91)
Extensions (16)
Effect (3)
NGUI (81)
UGUI (9)
Physics (2)
Shader (37)
Math (1)
Design Pattern (2)
Xml (1)
Tips (201)
Link (23)
World (1)
AssetBundle (25)
Mecanim (2)
Plugins (79)
Trouble Shooting (70)
Encrypt (7)
LightMap (4)
Shadow (4)
Editor (12)
Crash Report (3)
Utility (9)
UnityVS (2)
Facebook SDK (2)
iTween (3)
Font (13)
Ad (14)
Photon (2)
IAP (1)
Google (8)
Android (51)
iOS (44)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

Unity 2021.3.36f1

GoogleMobileAds 9.0.0

AppsFlyer 6.13.10

----

 

[추가] 2024-04-05

GDPR 동의가 엮이면서 '사용자 동의'를 받은 후로 앱 측정을 지연 시켜야 돼서,

GoogleMobileAdsSettings.asset에서 'Delay app measurement'를 체크해 줘야 되는 것 같다.

[참조] https://developers.google.com/admob/unity/privacy/gdpr?hl=ko

 

----

이번에 GDPR 동의 관련 작업을 하게 되면서 이런저런 엮이는 것들이 많았는데..

문제가 됐던 이슈 리스트 정리해 봄

 

1) GDPR 동의를 추가하고나니 iOS의 IDFA(AppTrackingTransparency) 동의와 내용이 겹친다고

    "앞에서 거부했는데 왜 뒤에 또 동의창을 뛰우냐~" 라는 내용으로 iOS 검수 리젝 됨.

     - 참고로 iOS ATT(IDFA)와 GDPR의 순서는 GDPR먼저 뛰우고 ATT(IDFA)를 판단하라고 함

https://developers.google.com/admob/android/privacy/gdpr?hl=ko

 

    그래서 확인해보니, Admob(GoogleMobileAds)에서 GDPR과 IDFA 둘 다 제공하고 Admob에서 진행하면 알아서 유기적으로 처리해준다.

    - [EEA( European Economic Area)인 경우] iOS에서 GDPR 동의 거부하면 IDFA 동의창 안 뜸, GDPR 동의하면 IDFA 동의 창 뜸.

    - [EEA( European Economic Area)가 아닌 경우] iOS에서 IDFA 안내 및 동의창만 뜸.

 

[참고] https://blueasa.tistory.com/2789

 

[검수리젝] GDPR/IDFA(ATT) 관련 검수 리젝

Unity 2021.3.35f1 GoogleMobileAds 8.7.0 ---- GDPR 동의 로직 추가하고 iOS 검수 넣었더니 리젝 됐다. 사유는 대충 정리하면, GDPR 팝업에서 '거부'를 했는데, 같은 이슈인 'IDFA(AppTrackingTransparency)' 동의 여부를

blueasa.tistory.com

 

2) AppsFlyer도 이번에 버전업(6.13.0) 해서 보니, GDPR 관련 설정 옵션(AppsFlyerConsent 클래스가 생김)이 생겼다.

    아래 예시와 같이 GDPR 정보 셋팅 후, AppsFlyer를 Initialize 하라고 한다.

// If the user is subject to GDPR - collect the consent data
    // or retrieve it from the storage
    ...
    // Set the consent data to the SDK:
    AppsFlyerConsent consent = AppsFlyerConsent.ForGDPRUser(true, true);
    AppsFlyer.setConsentData(consent);
        
    AppsFlyer.startSDK();

 

    자세한 내용은 아래 AppsFlyer 문서를 참고하자.

[참고] https://ko.dev.appsflyer.com/hc/docs/basicintegration

 

연동

AppsFlyerObject 프리팹을 사용하거나 수동으로 플러그인을 초기화할 수 있습니다. AppsFlyerObject.prefabManual의 integrationCollect IDFA를 ATTrackingManagerSending SKAN 포스트백과 함께 사용하여 AppsflyerMacOS initializat

ko.dev.appsflyer.com

 

3) 위 AppsFlyer 연동하려고 보니 아래와 같이 GDPR 일 때, 넘겨주는 매개변수 2개가 보인다.

    - hasConsentForDataUsage : GDPR 동의 상태

    - hasConsentForAdsPersonaliation : 광고 개인화 동의 상태

public static AppsFlyerConsent ForGDPRUser(bool hasConsentForDataUsage, bool hasConsentForAdsPersonalization)
{
    return new AppsFlyerConsent(true, hasConsentForDataUsage, hasConsentForAdsPersonalization);
}

public static AppsFlyerConsent ForNonGDPRUser()
{
    return new AppsFlyerConsent(false, false, false);
}

 

- hasConsentForDataUsage는 GDPR 동의 상태라서 Admob에도 정보가 있어서 받아오면 되겠는데,

- hasConsentForAdsPersonaliation은 GDPR 동의 창 세부 상태에서 광고 개인화 동의를 체크를 하는데 정보를 어떻게 받는지 몰라서 이리저리 찾아보다가 IABTCF_VendorConsents가 광고 개인화 관련 동의 값이란 걸 알게 됐다.

 

[참고] https://stackoverflow.com/questions/69307205/mandatory-consent-for-admob-user-messaging-platform

 

Mandatory Consent for Admob User Messaging Platform

I switched from the deprecated GDPR Consent Library to the new User Messaging Platform, and used the code as stated in the documentation. I noticed that when the user clicks on Manage Options then

stackoverflow.com

 

관련해서 잘 정리해 놓은 글을 찾아서 블로그에도 올려뒀다.

[링크] https://blueasa.tistory.com/2791

 

[펌] GDPR에 대해 알아보자 (Feat 애드몹)

1 얘들아 안녕, 다들 개발 잘 하고 있어? 저번 달에 GDPR 구현하다가 막혀서 여기다 글을 올렸었는데, 다행히 이젠 해결한 것 같아. 그 당시 해결하면 팁 남겨달라던 친구의 댓글이 기억나서 다시

blueasa.tistory.com

 

 

위의 링크글에서 동의 정보 관련에 필요한 CurrentGDPR 클래스 설명이 잘 돼 있다.

원래 CurrentGDPR 클래스는 GoogleMobileAds 8.6.0 이전 버전 기준으로 만들어져서,

GoogleMobileAds 8.7.0에 추가된 ApplicationPreferences를 사용해서 OS에 따라 분기하지 않아도 되도록 수정해서 아래 올려둔다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System.Linq;
using GoogleMobileAds.Api;

public static class CurrentGDPR
{
    private static bool _isGdprOn;
    private static string _purposeConsent, _vendorConsent, _vendorLi, _purposeLi, _partnerConsent;

    static CurrentGDPR()
    {
        InitData();
    }

    public static void InitData()
    {
        int gdprNum = 0;

        // [GoogleMobileAds 8.7.0]에서 ApplicationPreferences 추가됨
        // [참고] https://stackoverflow.com/questions/77838024/admob-gdpr-ump-issue-empty-iab-tcf-strings-on-android-after-user-consent
        // [참고] https://developers.google.com/admob/android/privacy/gdpr?hl=ko
        gdprNum = ApplicationPreferences.GetInt("IABTCF_gdprApplies");
        _purposeConsent = ApplicationPreferences.GetString("IABTCF_PurposeConsents");
        _vendorConsent = ApplicationPreferences.GetString("IABTCF_VendorConsents");
        _vendorLi = ApplicationPreferences.GetString("IABTCF_VendorLegitimateInterests");
        _purposeLi = ApplicationPreferences.GetString("IABTCF_PurposeLegitimateInterests");
        _partnerConsent = ApplicationPreferences.GetString("IABTCF_AddtlConsent");

        #region [GoogleMobileAds 8.6.0 이전] 버전
//#if UNITY_EDITOR // 에디터에서는 자바 호출이 에러나서 에외처리
//        gdprNum = 1;
//        _purposeConsent = "0000000000";
//        _vendorConsent = "0000000000";
//        _vendorLi = "";
//        _purposeLi = "";
//        _partnerConsent = "";
//#elif UNITY_ANDROID
//        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
//        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
//        AndroidJavaClass preferenceManagerClass = new AndroidJavaClass("android.preference.PreferenceManager");
//        AndroidJavaObject sharedPreferences = 
//                 preferenceManagerClass.CallStatic<AndroidJavaObject>("getDefaultSharedPreferences", currentActivity);

//        gdprNum = sharedPreferences.Call<int>("getInt", "IABTCF_gdprApplies", 0);
//        _purposeConsent = sharedPreferences.Call<string>("getString", "IABTCF_PurposeConsents", "");
//        _vendorConsent = sharedPreferences.Call<string>("getString", "IABTCF_VendorConsents", "");
//        _vendorLi = sharedPreferences.Call<string>("getString", "IABTCF_VendorLegitimateInterests", "");
//        _purposeLi = sharedPreferences.Call<string>("getString", "IABTCF_PurposeLegitimateInterests", "");
//        _partnerConsent = sharedPreferences.Call<string>("getString", "IABTCF_AddtlConsent", "");
//#elif UNITY_IOS
//        gdprNum = PlayerPrefs.GetInt("IABTCF_gdprApplies", 0);
//        _purposeConsent = PlayerPrefs.GetString("IABTCF_PurposeConsents", "");
//        _vendorConsent = PlayerPrefs.GetString("IABTCF_VendorConsents", "");
//        _vendorLi = PlayerPrefs.GetString("IABTCF_VendorLegitimateInterests", "");
//        _purposeLi = PlayerPrefs.GetString("IABTCF_PurposeLegitimateInterests", "");
//        _partnerConsent = PlayerPrefs.GetString("IABTCF_AddtlConsent", "");
//#endif
        #endregion

        // 0 이면 아예 GDPR 대상이 아님. 1이어야 GDPR
        if (gdprNum == 1)
            _isGdprOn = true;
        else
            _isGdprOn = false;

        Debug.Log("GDPR을 띄우는가? " + _isGdprOn);
        Debug.Log("광고에 필요한 권한 동의: " + _purposeConsent);
        Debug.Log("광고에 필요한 적법관심(광고 개인화?) 동의: " + _vendorConsent);
        Debug.Log("구글에 동의처리가 되어있는가?: " + _vendorLi);
        Debug.Log("구글에 적법관심(광고 개인화?) 처리 여부: " + _purposeLi);
        Debug.Log("파트너 네트워크 여부: " + _partnerConsent);
    }

/// <summary>
    /// GDPR을 띄워야 할 유저인지(EEA = 유럽 + 영국) 리턴
    /// </summary>
    /// <returns>EEA 여부</returns>
    public static bool IsGDPR()
    {
        return _isGdprOn;
    }

    /// <summary>
    /// 광고에 필요한 권한 동의 여부
    /// </summary>
    /// <returns>동의 여부</returns>
    public static bool IsPurposeConsents()
    {
        // Example value:
        // 동의   : "1111111111"
        // 비동의 : "0"

        if (true == string.IsNullOrEmpty(_purposeConsent))
        {
            return false;
        }

        Debug.LogFormat("[IsPurposeConsents] {0}", HasAttribute(_purposeConsent, 1));

        // Purposes are zero-indexed. Index 0 contains information about Purpose 1.
        // _purposeConsent[0]에 1(동의)이 있는지 체크
        return HasAttribute(_purposeConsent, 1);
    }

    /// <summary>
    /// 광고에 필요한 적법관심(광고 개인화?) 동의 여부
    /// </summary>
    /// <returns>동의 여부</returns>
    public static bool IsVendorConsents()
    {
        if (true == string.IsNullOrEmpty(_vendorConsent))
        {
            return false;
        }

        // _vendorConsent[0]에 1(동의)이 있는지 체크
        return HasAttribute(_vendorConsent, 1);
    }

    // 광고가 보여지는지 여부 리턴
    public static bool CanAdShow()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        // 광고 가능 - 비개인화 광고
        // return HasConsentFor(new List<int> { 1 }, _purposeConsent, hasGoogleVendorConsent)
        //        && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 }, 
        //            _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);

        // 광고 가능 - 제한적인 광고 - 1에 대한 권한이 없어도 됨 ㅇㅇ
        return HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 },
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    // 개인화 광고가 보여지는지 여부 리턴
    public static bool CanShowPersonalizedAds()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        return HasConsentFor(new List<int> { 1, 3, 4 }, _purposeConsent, hasGoogleVendorConsent)
               && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 },
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    public static bool IsPartnerConsent(string partnerID) // 파트너 권한 있는지 확인
    {
        return _partnerConsent.Contains(partnerID);
    }

    // 이진 문자열의 "index" 위치에 "1"이 있는지 확인합니다(1 기반).
    private static bool HasAttribute(string input, int index)
    {
        return (index <= input.Length) && (input[index - 1] == '1');
    }

    // 목적 목록에 대한 동의가 주어졌는지 확인합니다.
    private static bool HasConsentFor(List<int> purposes, string purposeConsent, bool hasVendorConsent)
    {
        return purposes.All(p => HasAttribute(purposeConsent, p)) && hasVendorConsent;
    }

    // 목적 목록에 대한 공급자의 동의 또는 정당한 이익이 있는지 확인합니다.
    private static bool HasConsentOrLegitimateInterestFor(List<int> purposes, string purposeConsent, string purposeLI, bool hasVendorConsent, bool hasVendorLI)
    {
        return purposes.All(p =>
            (HasAttribute(purposeLI, p) && hasVendorLI) ||
            (HasAttribute(purposeConsent, p) && hasVendorConsent));
    }
}

 

 

[결론]

여기서 내가 필요했던

AppsFlyer에 넘기려던 정보는 CurrentGDPR.CanShowPersonalizedAds() 였다.

CurrentGDPR .IsGDPR()도 써도 될 것 같긴한데..

GoogleMobileAds에서 ConsentInformation.ConsentStatus를 사용중인데 뭘 써야될지는 좀 테스트 봐야 될 것 같다.

 

 

  [초기화 순서]

1. GoogleMobileAds -

    1.1. EEA 일 경우

        1.1.1. GDPR 동의 진행

            1.1.1.1. GDPR 동의 하면, (iOS만)IDFA 동의 진행(GDPR 동의 요청하면 IDFA도 필요하면 자동으로 진행 된다.)

            1.1.1.2. GDPR 동의 안하면, IDFA 동의 Skip(IDFA도 거부로 판단)

    1.2. EEA가 아닐 경우

        1.2.1. GDPR 동의 진행 안함

        1.2.2. (iOS만) IDFA 동의 진행

    1.3. GoogleMobileAds 초기화 진행

 

2. GDPR / (iOS만)IDFA 동의 진행 완료 후, AppsFlyer 초기화 진행

    2.1. AppsFlyerConsent.ForGDPRUser() || AppsFlyerConsent.ForNonGDPRUser() 셋팅

    2.2. AppsFlyer.startSDK() 실행

 

 

[추가] GDPR 동의 여부 읽는 방법(위 소스에서 IsPurposeConsents() 함수 참조)

GDPR 동의가 수집된 후에는 TCF v2 사양에 따라 로컬 저장소에서 동의 여부를 읽을 수 있습니다. IABTCF_PurposeConsents 키는 각 TCF 목적에 대한 동의를 나타냅니다.

다음 코드 스니펫은 목적 1에 대한 동의를 확인하는 방법을 보여줍니다.

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
// Example value: "1111111111"
String purposeConsents = sharedPref.getString("IABTCF_PurposeConsents", "");
// Purposes are zero-indexed. Index 0 contains information about Purpose 1.
if (!purposeConsents.isEmpty()) {
  String purposeOneString = purposeConsents.charAt(0);
  boolean hasConsentForPurposeOne = purposeOneString.equals("1");
}

[출처] https://developers.google.com/admob/android/privacy/gdpr?hl=ko

 

GDPR IAB 지원  |  Android  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. GDPR IAB 지원 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 가이드에서는 UMP SDK의 일부로 GDPR IAB TCF

developers.google.com

 

 

 

 

[참조] https://developers.google.com/admob/unity/privacy/gdpr?hl=ko

[참조] https://stackoverflow.com/questions/69307205/mandatory-consent-for-admob-user-messaging-platform

[참조] https://stackoverflow.com/questions/77838024/admob-gdpr-ump-issue-empty-iab-tcf-strings-on-android-after-user-consent

[참조] https://stackoverflow.com/questions/69307205/mandatory-consent-for-admob-user-messaging-platform

[참조] https://gall.dcinside.com/mgallery/board/view/?id=game_dev&no=150987

[참조] https://developers.google.com/admob/unity/privacy/gdpr?hl=ko

[참조] https://ko.dev.appsflyer.com/hc/docs/basicintegration

반응형
Posted by blueasa
, |
1

 

얘들아 안녕, 다들 개발 잘 하고 있어?

 

 

저번 달에 GDPR 구현하다가 막혀서 여기다 글을 올렸었는데, 다행히 이젠 해결한 것 같아.

 

그 당시 해결하면 팁 남겨달라던 친구의 댓글이 기억나서 다시 돌아왔어.

 

 

사실 GDPR은 나온지가 진짜 오래 되었어.

 

심지어 구글 애드몹 기준으로도 ‘24년 1월 16일까지 대응 필수!’라며 경고를 엄청 띄워왔었기에 대부분 대응을 완료 했을 거라고 생각해.

 

그래도 이제 개발하는 친구들은 모를 수도 있을테니 도움이 되었으면 하는 마음에 남겨봐.

 

 

그럼 시작해보자.

 

 

 

 

GDPR이 뭐임?

 

정확한 명칭은 유럽 개인정보 보호법이라는데, 간단히 이야기하면

 

“유럽 유저들의 개인 정보를 가져다 쓰려면 직접 허락을 받아라’”

 

라고 할 수 있어. 애플의 ATT와 비슷한데, 조건이 안 맞으면 광고를 아예 못 튼다는 부분에서 조금 더 빡센 느낌이야.

 

더 상세한 내용이 궁금한 친구들은 구글에 GDPR로 검색하면 자료가 쏟아지니까 확인해보자.

 

사실 인디 게임 개발자인 우리가 알아야 될 것은 하나인 것 같아.

 

“유럽(+ 영국)에서 광고로 돈 벌려면 GDPR 동의 팝업을 추가해야 함.”

 

 

 

 

 

GDPR 대응 안하면 어떻게 되는데?

 

중요한 건 GDPR의 대상이 그 이름처럼 유럽(+영국) 한정이라는거야.

 

즉, 아직 글로벌 서비스를 계획하고 있지 않다면 신경 쓸 필요가 없어.

 

하지만 서비스의 대상에 유럽(+영국)가 포함된다면, GDPR 동의를 받지 않은 유저들에겐 광고를 아예 띄울 수 없어.

 

다만 이미 서비스 중인 게임 기준, 따로 GDPR을 구현하지 않더라도 어떻게든 애드몹이 온몸 비틀기로 GDPR 동의를 띄우는 것 같아.

 

다만 제대로 된 팝업은 아닌 것으로 보이고, 그래서인지 동의율이 낮아.

 

거기다 애드몹이 이런 식의 땜빵을 꾸준히 해준다는 보장도 없으니, 아직 대응을 안했거나 신규 개발 중이라면 GDPR 처리를 해 두는걸 권장해.

 

 

 

 

 

그렇다면 어떻게 대응하는가?

 

앞에서 말했듯이 애드몹에서 최근에 필수로 변경해서 그렇지, GDPR 자체는 몇 년도 전에 있었어.

 

때문에 GDPR을 처리할 수 있는 방법은 다양해.

 

애드몹처럼 광고 플랫폼이 제공하는 기능을 사용해도 되고, 아예 GDPR을 별도로 처리해주는 서비스도 있다고 들었어.

 

근데 난 애드몹으로 처리했으니, 애드몹 기준으로 설명할게.

 

애드몹에서 GDPR을 처리하려면, 크게 2가지가 필요해.

 

바로 팝업과 구현이야.

 

 

 

 

 

 

팝업 추가하기

 

 

2

먼저 애드몹의 ‘개인 정보 보호 및 메시지’ 메뉴로 들어가서, 유럽 규정의 ‘관리’로 들어가.

 

 

3

 

그럼 이렇게 메시지 만들기를 선택할 수 있고

 

 

4

 

이후에 나오는 페이지에서 GDPR 페이지 설정을 마무리하면 돼.

 

 

여기서 신경 써야 할 것은 3가지인 것 같아.

 

1, 2 - 둘 다 유저에게 미동의 버튼을 얼마나 적극적으로 보여주느냐를 결정하는 기능이야.

개발자 입장에선 동의율이 높은 게 좋으니 둘 다 사용안함으로 두는 게 유리할 거야.

 

3 - 해당 팝업을 몇 가지 언어로 지원하는지를 결정하는 부분이야.

기본은 영어로 되어있고, 다양한 언어를 지원하길래 나는 31개 추가언어를 모두 활성화했어.

 

여기까지 세팅하고 ‘게시’ 버튼 누르면 팝업에 대한 세팅은 끝이야.

 

 

 

 

 

팝업 구현하기

 

저것만 추가하고 끝나면 참 좋은데... 안타깝게도 코드 딴에서 직접 저 팝업을 호출해줘야 하더라.

 

애드몹에서 직접 설명하고 있고, 코드가 복잡하지 않으니 직접 확인해보면 될 거야.

 

https://developers.google.com/admob/unity/privacy?hl=ko

 

하면서 내가 겪었던 문제를 몇 가지 공유하면 다음과 같아.

 

- 싱글 스레드 에러가 발생하면 아래 코드를 추가해 줘야 함.

  MobileAds.RaiseAdEventsOnUnityMainThread = true;

 

- 테스트를 위해 핸드폰의 Hashed ID가 필요한데, 테스트 빌드를 로그캣에 물려서 돌려보면 로그에 찍힘.

 

이렇게 코드까지 추가해주면 기본적인 작업은 끝이야.

 

 

 

 

 

추가 처리 (선택)

 

앞선 2개만 처리하면 돌리는 것은 문제가 없어. 그러니 대부분의 경우엔 이 정도에서 구현을 마쳐도 괜찮을거야.

 

하지만 내 경우는 상세한 정보를 필요했는데, 이런 것들이야.

 

- 이 유저가 GDPR의 대상인지 아닌지

- 지금 애드몹이 광고를 안주는게 유저가 GDPR 동의를 안 해서 그런 건지, 그냥 광고 슬롯이 빈 건지

- 개인화 광고 여부

 

특히 ‘주모 키우기’에선 광고 슬롯이 비어 있는 게 유저 잘못은 아니라고 생각해서 리워드 보상을 주고 있었단 말야.

 

하지만 GDPR에 비동의한 유저도 광고 슬롯은 똑같이 비어있는 것으로 확인되었고, 때문에 GDPR의 동의 상태 확인이 무척 중요해졌지.

 

저번에 문의 글을 남긴 이유도 이 부분의 방법을 찾지 못해서 그런거였어.

 

하지만 열심히 뒤지다보니 다 방법이 있긴 하더라.

 

여기서부터는 링크로 대체할게.

 

 

애드몹에서 GDPR 동의 수준 확인하는 법

https://stackoverflow.com/questions/69307205/mandatory-consent-for-admob-user-messaging-platform

 

유니티에서 자바 클래스 호출해서 확인하는 법

https://groups.google.com/g/google-admob-ads-sdk/c/uIQkJX6_XtM/m/KIFmbfXVAQAJ

 

애드몹의 파트너 플랫폼 별 ID 확인 (앱로빈 - 1301, 유니티애즈 - 3234)

https://support.google.com/admob/answer/9681920?hl=en // 이 페이지의 Where will the Google ATPs be published? 메뉴에서 받음

 

 

여기 글들을 잘 읽어보면 대응이 가능하긴 한데... 솔직히 나도 엄청 헤맸다보니 사람에 따라선 부족할 수 있다 싶어.

 

그러니 내가 구현한 코드도 함께 남겨둘게. 이해가 어려운 친구들은 참고해 봐.

    private void Start()

    {       
        MobileAds.RaiseAdEventsOnUnityMainThread = true; // 애드몹 관련 처리는 메인 스레드에서만 처리하도록 처리

        #if !UNITY_EDITOR // 에디터에선 GDPR 동의 홀드
            Debug.Log("GDPR 동의 프로세스 스타트");
            #if DEBUGBUILD // 테스트 빌드에서만 테스트모드 활성화
                Debug.Log("테스트 버전 - 초기화 후 체크 개시!");
                ConsentInformation.Reset(); // 테스트를 위해 기존 GDPR 정보 초기화
                var debugSettings = new ConsentDebugSettings
                {
                    DebugGeography = DebugGeography.EEA, // 일시적으로 유럽인척
                    #if UNITY_IOS
                        TestDeviceHashedIds = new List<string> {"여기에 아이폰 해시 ID"}
                    #else
                        TestDeviceHashedIds = new List<string> {"여기에 안드로이드 해시 ID"}
                    #endif                
                };
                ConsentRequestParameters request = new ConsentRequestParameters {ConsentDebugSettings = debugSettings};
                ConsentInformation.Update(request, OnConsentInfoUpdated);
            #else // 릴리즈 & 디스트리뷰트에선 GDPR 테스트 모드 끄기
                Debug.Log("릴리즈 버전 - 체크 개시!");
                ConsentRequestParameters request = new ConsentRequestParameters();
                ConsentInformation.Update(request, OnConsentInfoUpdated);
            #endif
        #else       
            // 구글 애즈 초기화
            Debug.Log("애드몹 초기화 시도!");
            MobileAds.Initialize(initStatus =>
            {
                StartAdSet();
            });
        #endif
    }

    void OnConsentInfoUpdated(FormError consentError)
    {
        Debug.Log("GDPR 동의 상태 콜백 확인");
        if (consentError != null)
        {
            Debug.Log("동의 상태 확인 실패: " + consentError);
            return;
        }
        Debug.Log("GDPR 동의 상태 확인 완료!");
        
        ConsentForm.LoadAndShowConsentFormIfRequired((FormError formError) =>
        {
            Debug.Log("양식 로드 시도");
            if (formError != null)
            {
                Debug.Log("동의 획득 실패: " + consentError);
                return;
            }
          
            if (CurrentGdpr.IsGDPR()) // GDPR을 검사하는 국가에서만 체크
            {
                isNoAd = !CurrentGdpr.CanAdShow();
                if (CurrentGdpr.IsPartnerConsent("1301")) // 앱로빈 확인
                { 
                    AppLovin.SetHasUserConsent(true);
                    Debug.Log("앱로빈 GDPR 켜짐");
                }
                
                if (CurrentGdpr.IsPartnerConsent("3234")) // 유니티애즈 확인
                { 
                    UnityAds.SetConsentMetaData("gdpr.consent", true);
                    Debug.Log("유니티애즈 GDPR 켜짐");
                }
            }
            else
                isNoAd = false;

            Debug.Log("GDPR 적용 여부: " + CurrentGdpr.IsGDPR());
            Debug.Log("동의 성공. 현재 광고 재생 가능? " + !isNoAd);
            Debug.Log("개인화된 광고 가능? " + CurrentGdpr.CanShowPersonalizedAds());
            
            if (ConsentInformation.CanRequestAds())
            {
                // 구글 애즈 초기화
                Debug.Log("애드몹 초기화 시도!");
                MobileAds.Initialize(initStatus =>
                {
                    StartAdSet();
                });
            }
            else
                Debug.Log("광고 요청 불가 상태...");
        });
    }

 

여기까지가 GDPR 세팅 및 팝업 호출 & 애드몹 초기화고

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System.Linq;

public static class CurrentGdpr
{
    private static bool _isGdprOn;
    private static string _purposeConsent, _vendorConsent, _vendorLi, _purposeLi, _partnerConsent;
    
    static CurrentGdpr()
    {
        SetData();
    }
    
    public static void SetData()
    {
        int gdprNum;

#if UNITY_EDITOR // 에디터에서는 자바 호출이 에러나서 에외처리
        gdprNum = 1;
        _purposeConsent = "0000000000";
        _vendorConsent = "0000000000";
        _vendorLi = "";
        _purposeLi = "";
        _partnerConsent = "";
#elif UNITY_ANDROID
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
        AndroidJavaClass preferenceManagerClass = new AndroidJavaClass("android.preference.PreferenceManager");
        AndroidJavaObject sharedPreferences = 
                 preferenceManagerClass.CallStatic<AndroidJavaObject>("getDefaultSharedPreferences", currentActivity);

        gdprNum = sharedPreferences.Call<int>("getInt", "IABTCF_gdprApplies", 0);
        _purposeConsent = sharedPreferences.Call<string>("getString", "IABTCF_PurposeConsents", "");
        _vendorConsent = sharedPreferences.Call<string>("getString", "IABTCF_VendorConsents", "");
        _vendorLi = sharedPreferences.Call<string>("getString", "IABTCF_VendorLegitimateInterests", "");
        _purposeLi = sharedPreferences.Call<string>("getString", "IABTCF_PurposeLegitimateInterests", "");
        _partnerConsent = sharedPreferences.Call<string>("getString", "IABTCF_AddtlConsent", "");
#elif UNITY_IOS
        gdprNum = PlayerPrefs.GetInt("IABTCF_gdprApplies", 0);
        _purposeConsent = PlayerPrefs.GetString("IABTCF_PurposeConsents", "");
        _vendorConsent = PlayerPrefs.GetString("IABTCF_VendorConsents", "");
        _vendorLi = PlayerPrefs.GetString("IABTCF_VendorLegitimateInterests", "");
        _purposeLi = PlayerPrefs.GetString("IABTCF_PurposeLegitimateInterests", "");
        _partnerConsent = PlayerPrefs.GetString("IABTCF_AddtlConsent", "");
#endif
        // 0 이면 아예 GDPR 대상이 아님. 1이어야 GDPR
        if (gdprNum == 1)
            _isGdprOn = true;
        else  
            _isGdprOn = false;
        
        Debug.Log("GDPR을 띄우는가? " + _isGdprOn);
        Debug.Log("광고에 필요한 권한 동의: " + _purposeConsent);
        Debug.Log("광고에 필요한 적법관심(?) 동의: " + _vendorConsent);
        Debug.Log("구글에 동의처리가 되어있는가?: " + _vendorLi);
        Debug.Log("구글에 적법관심(?) 처리 여부: " + _purposeLi);
        Debug.Log("파트너 네트워크 여부: " + _partnerConsent);
    }

    // GDPR을 띄워야 할 유저인지(= 유럽 + 영국) 리턴
    public static bool IsGDPR()
    {
        return _isGdprOn;
    }

    // 광고가 보여지는지 여부 리턴
    public static bool CanAdShow()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        // 광고 가능 - 비개인화 광고
        // return HasConsentFor(new List<int> { 1 }, _purposeConsent, hasGoogleVendorConsent)
        //        && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 }, 
        //            _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
        
        // 광고 가능 - 제한적인 광고 - 1에 대한 권한이 없어도 됨 ㅇㅇ
        return HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 }, 
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    // 개인화 광고가 보여지는지 여부 리턴
    public static bool CanShowPersonalizedAds()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        return HasConsentFor(new List<int> { 1, 3, 4 }, _purposeConsent, hasGoogleVendorConsent)
               && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 }, 
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    public static bool IsPartnerConsent(string partnerID) // 파트너 권한 있는지 확인
    {
        return _partnerConsent.Contains(partnerID);
    }
    
    // 이진 문자열의 "index" 위치에 "1"이 있는지 확인합니다(1 기반).
    private static bool HasAttribute(string input, int index)
    {
        return input.Length >= index && input[index - 1] == '1';
    }
    
    // 목적 목록에 대한 동의가 주어졌는지 확인합니다.
    private static bool HasConsentFor(List<int> purposes, string purposeConsent, bool hasVendorConsent)
    {
        return purposes.All(p => HasAttribute(purposeConsent, p)) && hasVendorConsent;
    }
    
    // 목적 목록에 대한 공급자의 동의 또는 정당한 이익이 있는지 확인합니다.
    private static bool HasConsentOrLegitimateInterestFor(List<int> purposes, string purposeConsent, string purposeLI, bool hasVendorConsent, bool hasVendorLI)
    {
        return purposes.All(p =>
            (HasAttribute(purposeLI, p) && hasVendorLI) ||
            (HasAttribute(purposeConsent, p) && hasVendorConsent));
    }
}

이건 GDPR에 대한 세부 속성을 확인하는 코드야.

 

위의 링크에 있는 코드들을 C#으로 변경하고, 최신 상황에 맞춰 수정한거야.

 

 

 

 

 

 

 

 

자, 대충 여기까지야.

 

나 또한 구현에 시간이 걸리다보니 애드몹이 말한 제한시간을 넘겨서 GDPR을 추가하게 되었어.

 

그러다보니 본의 아니게 애드몹 기본 제공 GDPR을 몇 일간 사용하게 되었었는데, 차이는 다음과 같아.

 

 

5
6

 

위쪽이 애드몹에서 제공하는 기본 구현일 때, 아래쪽이 지금 상황이야. 1주일 남짓인데 그 사이에 레이아웃이 바뀌었네. 흠?;

 

데이터가 적어 객관성은 떨어지지만, 일단 구현을 처리한 쪽이 동의율이 높긴 한 것 같아.

 

위에서 따로 이야기는 안했지만, GDPR 동의를 끈 유저들이 광고 시청을 시도할 때는 '옵션에서 GDPR을 켜!' 라고 안내도 하고 있어.

 

 

 

 

 

어때, 조금 도움이 되었을까?

 

처음 게임을 출시할 때 GDPR 같은 것은 신경 안써도 되었었는데, 어째 점점 챙겨야 될 게 많아지는 느낌이네.

 

1주일 넘게 서비스하면서 문제를 발견하진 못했지만, 맨 땅에 헤딩하면서 찾은 정보다 보니 틀린 내용도 있을 수 있어.

 

혹시 잘 아는 친구들은 수정사항을 댓글로 남겨주면 맞춰서 반영할게.

 

자 그럼 다들 개발 열심히 해!

 

 

[출처] https://gall.dcinside.com/mgallery/board/view/?id=game_dev&no=150987

 

GDPR에 대해 알아보자 (Feat 애드몹) - 인디 게임 개발 마이너 갤러리

얘들아 안녕, 다들 개발 잘 하고 있어?저번 달에 GDPR 구현하다가 막혀서 여기다 글을 올렸었는데, 다행히 이젠 해결한 것 같아.그 당시 해결하면 팁 남겨달라던 친구의 댓글이 기억나서 다시 돌

gall.dcinside.com

 

반응형
Posted by blueasa
, |

Untiy 2021.3.42f1

GoogleMobileAds 9.1.0

----

 

[추가] 2024-08-22

GoogleMobileAds의 UMP로 GDPR과 IDFA(ATT)를 유기적으로 처리는 가능한데,

GoogleMobileAds의 IDFA(ATT)를 사용하면 안이쁜 '무료 이용'어쩌고 팝업이 한 번 뜬다.

이 부분을 없애기 위해서 ATT를 GoogleMobileAds의 IDFA를 Off 하고, Unity Package인 'iOS 14 Advertising Support'를 사용하도록 변경했다.

[참고] https://docs.unity3d.com/Packages/com.unity.ads.ios-support@1.0/manual/index.html

 

변경한 소스는 아래 소스에 갱신하고, 관련해서 CurrentGDPR.cs도 추가했다.

로직 순서는 대충 아래와 같다.

 

[순서] 애드몹(GoogleMobileAds) 초기화 -> CurrentGDPR 초기화 ->  ATT(IDFA) 초기화

 

// GoogleMobileAds
yield return new WaitUntil(() => true == blueasa.GoogleMobileAdsManagerSGT.Exists);
yield return null;
SGT.Ads.Initialize();
yield return new WaitUntil(() => true == SGT.Ads.Initialized);
Debug.LogWarning("[GoogleMobileAds] 초기화 완료");
yield return null;
// GDPR 관련 동의 상태 Load[blueasa / 2024-03-21]
CurrentGDPR.InitData();
Debug.LogWarning("[CurrentGDPR] 초기화 완료");
yield return null;
// ATT(IDFA) 동의 체크
SGT.Ads.RequestAuthorizationTracking();
Debug.LogWarning("[ATT] 동의 체크 완료");
yield return null;

 

순서만 참조하면 될 듯 한데, 간단히 설명하자면 아래와 같다.

 

1) GoogleMobileAds를 초기화하면서 GDPR 동의 내부에서 진행.

2) CurrentGDPR 초기화해서 GDPR 관련 데이터 Load

3) Load 한 CurrentGDPR 데이터 참조해서 ATT 요청할지 판단 및 실행(RequestAuthorizationTracking)

 

추가적으로, GDPR 동의했어도, ATT 거부를 했다면,

설정창 등에 보여주던 GDPR 동의상태 변경을 위한 버튼을 띄우면 안된다고 한다.

(Apple 검수 넣었다가 해당 이슈로 리젝 당함)

 

그래서 IsAuthorizationTracking() 함수를 이용해서 ATT 거부 상태면, GDPR 설정 버튼이 안뜨도록 해뒀다.

참고하자.

 

----------------------------------------------------------------

2024년 1월 16일까지(참조:https://support.google.com/admob/answer/14189727?hl=ko) Admob을 사용하려면 유저에게 GDPR 동의를 받으라고 하는 것 같다.

그래서 Google에서 관련 SDK를 내놓은게 UMP(User Messaging Platform) SDK다.

----

 

Unity GoogleMobileAds에 UMP 적용하기 위해 찾아보니 누군가가 github에 정리해 놨길래 가져와서 적당히(?) 개조해서 내 GoogleMobileAdsManager에 추가 했다.

 

[Unity-Google-UMP-Check] https://github.com/TheBossaaa/Unity-Google-UMP-Check

 

GitHub - TheBossaaa/Unity-Google-UMP-Check: Google Admob & UMP Working Script Example

Google Admob & UMP Working Script Example. Contribute to TheBossaaa/Unity-Google-UMP-Check development by creating an account on GitHub.

github.com

 

기본적으로 위 소스만 적용하고 폰에서 실행해보면 아래와 같은 에러가 나온다.

 

[Error Code] GoogleMobileAds.Ump.Api.FormError

 

해당 에러는 GoogleMobileAds Dashboard에서 GDPR 관련 설정이 안돼 있어서 나오는 에러이다.

아래 Admob UMP Docs에서는 딱히 GoogleMobileAds Dashboard 관련 설정을 설명 안해놔서 모르고 넘어갈 수 있다.

Dashboard에 설정을 하자.

애드몹 대쉬보드 - 개인 정보 보호 및 메시지 - GDPR

 

[FormError 관련 참조] https://github.com/googleads/googleads-mobile-unity/issues/2780

 

GoogleMobileAds.Ump.Api.FormError · Issue #2780 · googleads/googleads-mobile-unity

[REQUIRED] Step 1: Describe your environment Unity version: 2021.3.27f1 Google Mobile Ads Unity plugin version: 8.0.0 Platform: Android Platform OS version: Android 10 Any specific devices issue oc...

github.com

 

※ 추가적으로 Admob UMP Docs에 보면 Runtime에 GDPR 변경을 위한 버튼을 구현하는 샘플이 보인다.

    법적으로 Runtime에 GDPR 변경을 해줘야 되는거면 버튼도 별도로 추가해줘야 될 것 같다.

 

[참조] https://github.com/googleads/googleads-mobile-unity/issues/2780

 

GoogleMobileAds.Ump.Api.FormError · Issue #2780 · googleads/googleads-mobile-unity

[REQUIRED] Step 1: Describe your environment Unity version: 2021.3.27f1 Google Mobile Ads Unity plugin version: 8.0.0 Platform: Android Platform OS version: Android 10 Any specific devices issue oc...

github.com

 

[GDPR 테스트]

GDPR 테스트 관련 소스가 들어있는데, Test Device Hashed Id를 넣으라고 돼있고, 실행해보면 Logcat에 뜬다고 돼 있는데 안떠서 찾아보니 아래와 같은 내용이 있다.

(난 현재 테스트 폰이 API 30이 안돼서 Test Device Hashed Id는 안넣어도 되는 것 같다.)

 

I also struggled with the same problem. The reason NOT_REQUIRED is always returned is that the test device ID has not been registered. DebugGeography does not apply to non-test devices. In my case, even if it is a basic Android emulator, I had to register the test device ID to resolve this issue. Your emulator's device ID is displayed in logcat as follows when ConsentInformation.requestConsentInfoUpdate is called.

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

EDIT: I did more tests. It requires a test device ID since Android API 30. Until API 29, it is not necessary to specify the test device ID. I'm not sure if this is only for my development environment.

[참조] https://stackoverflow.com/questions/68529268/ump-consentstatus-always-notrequired-when-testing

 

UMP ConsentStatus always "NotRequired" when testing

I have incorporated Google's UserMessagingPlatform into my Android project for user consent to be compliant with GDPR for AdMob. The problem is that the ConsentStatus always returns "NotRequired&

stackoverflow.com

 

[GoogleMobileAdsManager에 추가된 GDPR/IDFA(ATT) 관련 UMP 소스]

        using GoogleMobileAds.Ump.Api;


        #region UMP(User Messaging Platform)
        /// <summary>
        /// Privacy(GDPR) 요청
        /// Ensures that privacy and consent information is up to date.
        /// </summary>
        public void InitializeGoogleMobileAdsConsent()
        {
            // Test GDPR
            //GDPRDebugger();

            RequestConsentInfo();
        }

        ///Summary
        ///Request Consent Information
        ///Warning: Ads can be preloaded by the Google Mobile Ads SDK or mediation partner SDKs
        ///upon calling MobileAds.Initialize(). If you need to obtain consent from users in the European Economic Area (EEA), set any request-specific flags, such as tagForChildDirectedTreatment or tag_for_under_age_of_consent, or otherwise take action before loading ads.
        ///Ensure you do this before initializing the Google Mobile Ads SDK.
        ///Summary

        void RequestConsentInfo()
        {
            Debug.Log("[UMP] RequestConsentInfo()");

            // Set tag for under age of consent.
            // Here false means users are not under age of consent.
            ConsentRequestParameters request = new ConsentRequestParameters
            {
                TagForUnderAgeOfConsent = false,
                //ConsentDebugSettings = new ConsentDebugSettings
                //{
                //    // For debugging consent settings by geography.
                //    DebugGeography = DebugGeography.Disabled,
                //    // https://developers.google.com/admob/unity/test-ads
                //    TestDeviceHashedIds = TestDeviceIds,
                //}
            };

            // Check the current consent information status.
            ConsentInformation.Update(request, OnConsentInfoUpdated);
        }

        void OnConsentInfoUpdated(FormError consentError)
        {
            if (consentError != null)
            {
                // Handle the error.
                UnityEngine.Debug.LogErrorFormat("[UMP][OnConsentInfoUpdated] {0}", consentError);
                // UMP 동의 Error 시에도 초기화 진행[blueasa / 2024-03-15]
                InitializeGoogleMobileAds();
                return;
            }
            else
            {
                UnityEngine.Debug.LogFormat("Google Mobile Ads consent updated: {0}", ConsentInformation.ConsentStatus);
            }

            // If the error is null, the consent information state was updated.
            // You are now ready to check if a form is available.
            ConsentForm.LoadAndShowConsentFormIfRequired((FormError formError) =>
            {
                if (formError != null)
                {
                    // Consent gathering failed.
                    UnityEngine.Debug.LogErrorFormat("[UMP][LoadAndShowConsentFormIfRequired] {0}", consentError);
                    // UMP 동의 Error 시에도 초기화 진행[blueasa / 2024-03-15]
                    InitializeGoogleMobileAds();
                    return;
                }

                // Consent has been gathered.
                if (ConsentInformation.CanRequestAds())
                {
                    // UMP 동의 이후 GoogleMobileAds 초기화[blueasa / 2023-12-14]
                    InitializeGoogleMobileAds();
                }
            });
        }

        /// <summary>
        /// Privacy(GDPR) Button 활성화 여부 체크
        /// (설정 창에 버튼 추가함)
        /// </summary>
        public bool CheckActivePrivacyButton()
        {
            bool bActive = false;

            // EU인지 여부에 따라 버튼 활성화 체크 필요

            #region ConsentInformation.PrivacyOptionsRequirementStatus && AuthorizationTrackingStatus
            bActive = IsPrivacyOptionsRequirement() && IsAuthorizationTracking(); // Privacy && ATT 동의 체크
            //bActive = IsPrivacyOptionsRequirement();    // Privacy만 체크
            #endregion

            #region ConsentInformation.ConsentStatus
            //bActive = IsConsentForGDPR();
            #endregion

            return bActive;
        }

        /// <summary>
        /// 개인정보 보호 옵션을 표시해야 하는지 여부
        /// </summary>
        /// <returns></returns>
        public bool IsPrivacyOptionsRequirement()
        {
#if UNITY_EDITOR
            // [에디터] 무조건 비활성화 처리[blueasa / 2024-02-27]
            return false;
#endif
            // [PrivacyOptionsRequirementStatus enum 참조]
            // https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api#namespace_google_mobile_ads_1_1_ump_1_1_api_1a60f41ef4f7e14d5ae1fb5f23b7e0244b
            //public enum PrivacyOptionsRequirementStatus
            //{
            //    Unknown,
            //    NotRequired,
            //    Required
            //}

            bool bIsPrivacyOptionsRequirement = false;

            // 개인정보 보호 옵션을 표시해야 하는지 여부
            switch (ConsentInformation.PrivacyOptionsRequirementStatus)
            {
                // 개인정보 보호 옵션이 표시되어야 함.
                case PrivacyOptionsRequirementStatus.Required:
                    bIsPrivacyOptionsRequirement = true;
                    break;

                // 개인정보 보호 옵션은 표시할 필요가 없음.
                case PrivacyOptionsRequirementStatus.NotRequired:
                    bIsPrivacyOptionsRequirement = false;
                    break;

                // 개인 정보 보호 옵션 요구 사항 상태를 알 수 없음.
                case PrivacyOptionsRequirementStatus.Unknown:
                    bIsPrivacyOptionsRequirement = false;
                    break;

                default:
                    bIsPrivacyOptionsRequirement = false;
                    break;
            }
            Debug.LogWarningFormat("[PrivacyOptionsRequirementStatus] {0}", ConsentInformation.PrivacyOptionsRequirementStatus);

            return bIsPrivacyOptionsRequirement;
        }

        public bool IsConsentForGDPR()
        {
            // [ConsentStatus enum 참조]
            // https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api#namespace_google_mobile_ads_1_1_ump_1_1_api_1aa83ad2ecf6f2a08c584b60cef06f5133
            //ConsentStatus
            //{
            //    Unknown = 0,      // Unknown consent status.
            //    NotRequired = 1,  // Consent not required.
            //    Required = 2,     // User consent required but not yet obtained.
            //    Obtained = 3      // User consent obtained, personalized vs non-personalized undefined.
            //}

            bool bIsConsentForGDPR = false;

            switch (ConsentInformation.ConsentStatus)
            {
                case ConsentStatus.Unknown:     // 동의 상태를 알 수 없습니다.
                case ConsentStatus.Required:    // 사용자 동의가 필요하지만 아직 획득되지 않았습니다.
                case ConsentStatus.NotRequired: // 동의가 필요하지 않습니다.
                    bIsConsentForGDPR = false;
                    break;

                case ConsentStatus.Obtained:    // 사용자 동의 획득, 개인화 및 비개인화 정의되지 않음.
                    bIsConsentForGDPR = true;
                    break;
            }
            Debug.LogWarningFormat("[ConsentStatus] {0}", ConsentInformation.ConsentStatus);

            return bIsConsentForGDPR;
        }

        bool HasConsentFor(string strKey)
        {
            // Example value: "1111111111"
            string strPurpose = ApplicationPreferences.GetString(strKey);
            // Purposes are zero-indexed. Index 0 contains information about Purpose 1.
            if (!string.IsNullOrEmpty(strPurpose))
            {
                char cOneString = strPurpose[0];
                bool bHasConsentForOne = (cOneString == '1');

                return bHasConsentForOne;
            }

            return false;
        }

        /// <summary>
        /// Consent For GDPR
        /// </summary>
        /// <returns></returns>
        public bool HasConsentForPurpose()
        {
            return HasConsentFor("IABTCF_PurposeConsents");

            //// Example value: "1111111111"
            //string purposeConsents = ApplicationPreferences.GetString("IABTCF_PurposeConsents");
            //// Purposes are zero-indexed. Index 0 contains information about Purpose 1.
            //if (!string.IsNullOrEmpty(purposeConsents))
            //{
            //    char purposeOneString = purposeConsents[0];
            //    bool hasConsentForPurposeOne = (purposeOneString == '1');

            //    return hasConsentForPurposeOne;
            //}

            //return false;
        }

        /// <summary>
        /// Consent For Ads Personalization
        /// https://stackoverflow.com/questions/69307205/mandatory-consent-for-admob-user-messaging-platform
        /// </summary>
        /// <returns></returns>
        public bool HasConsentForVendor()
        {
            return HasConsentFor("IABTCF_VendorConsents");

            //// Example value: "1111111111"
            //string vendorConsents = ApplicationPreferences.GetString("IABTCF_VendorConsents");
            //// Purposes are zero-indexed. Index 0 contains information about Purpose 1.
            //if (!string.IsNullOrEmpty(vendorConsents))
            //{
            //    char purposeOneString = vendorConsents[0];
            //    bool hasConsentForVendorOne = (purposeOneString == '1');

            //    return hasConsentForVendorOne;
            //}

            //return false;
        }

        /// <summary>
        /// Privacy(GDPR) Button 클릭해서 GDPR 동의 창 띄우기
        /// </summary>
        public void OnClickPrivacyButton()
        {
            UpdatePrivacyButton();
        }

        void PrivacyButton()
        {
            //// Enable the privacy settings button.
            //if (_privacyButton != null)
            //{
            //    _privacyButton.onClick.AddListener(UpdatePrivacyButton);
            //    // Disable the privacy settings button by default.
            //    _privacyButton.interactable = false;
            //}
        }

        private void UpdatePrivacyButton()
        {
            // Logic for updating privacy options
            ShowPrivacyOptionsForm(); // You might want to call your method to show the privacy options form here
            Debug.LogFormat("[UMP][UpdatePrivacyButton] Privacy button clicked!");
        }

        /// <summary>
        /// Shows the privacy options form to the user.
        /// </summary>
        public void ShowPrivacyOptionsForm()
        {
            Debug.Log("[UMP] Showing privacy options form.");

            // PrivacyOptionsForm 무조건 팝업으로 변경[blueasa / 2023-12-14]
            ConsentForm.ShowPrivacyOptionsForm((FormError showError) =>
            //ConsentForm.LoadAndShowConsentFormIfRequired((FormError showError) =>
            {
                if (showError != null)
                {
                    Debug.LogErrorFormat("[UMP][LoadAndShowConsentFormIfRequired] Error showing privacy options form with error: {0}", showError.Message);
                }
                else
                {
                    // 버튼 상태 갱신(필요하면)

                    // Enable the privacy settings button.
                    //if (_privacyButton != null)
                    //{
                    //    _privacyButton.interactable =
                    //        ConsentInformation.PrivacyOptionsRequirementStatus ==
                    //        PrivacyOptionsRequirementStatus.Required;
                    //}
                }

            });
        }

        void GDPRDebugger()
        {
            Debug.Log("[UMP] GDPRDebugger()");

            ///Summary
            ///Use this for debugging
            ///

            // Define the test device ID for debugging
            string testDeviceHashedId = "0B030C0B27FA3A0A7FCF5766D3BBBA1A"; // Replace with your actual test device ID

            // Create debug settings for consent testing
            var debugSettings = new ConsentDebugSettings
            {
                TestDeviceHashedIds = new List<string>
                {
                    testDeviceHashedId
                }
            };

            // Set the debug geography for testing in the EEA
            debugSettings.DebugGeography = DebugGeography.EEA;
            Debug.Log("[UMP] GDPRDebugger Set : DebugGeography.EEA");

            // Set tag for under the age of consent.
            // Here false means users are not under the age of consent.
            ConsentRequestParameters request = new ConsentRequestParameters
            {
                TagForUnderAgeOfConsent = false,
                ConsentDebugSettings = debugSettings,
            };

            // Check the current consent information status.
            ConsentInformation.Update(request, OnConsentInfoUpdated);
        }

        /// <summary>
        /// Request ATT(IDFA)
        /// [Package] iOS 14 Advertising Support v1.0.0
        /// </summary>
        public void RequestAuthorizationTracking()
        {
#if UNITY_IOS
            Debug.Log("Unity iOS Support: Requesting iOS App Tracking Transparency native dialog.");
            
            var status = Unity.Advertisement.IosSupport.ATTrackingStatusBinding.GetAuthorizationTrackingStatus();
            Debug.LogWarningFormat("[RequestAuthorizationTracking-AuthorizationTrackingStatus] {0}", status);

            // 결정되지 않은 상태일 때만 체크 및 요청
            if (status == Unity.Advertisement.IosSupport.ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED)
            {
                bool bNeedRequest = false;
                if (false == CurrentGDPR.IsGDPR())
                {
                    // [Non EEA] ATT 동의 요청
                    bNeedRequest = true;
                }
                else
                {
                    // [EEA]
                    if (true == CurrentGDPR.IsPurposeConsents())
                    {
                        // [GDPR 동의] ATT 동의 요청
                        bNeedRequest = true;
                    }
                    else
                    {
                        // [GDPR 비동의] ATT 동의 체크가 필요없음
                        bNeedRequest = false;
                    }
                }

                // ATT 동의 요청 필요
                if (true == bNeedRequest)
                {
                    Unity.Advertisement.IosSupport.ATTrackingStatusBinding.RequestAuthorizationTracking();
                }
            }
#else
            Debug.LogWarning("Unity iOS Support: Tried to request iOS App Tracking Transparency native dialog, " +
                             "but the current platform is not iOS.");
#endif
        }

        /// <summary>
        /// ATT 동의 여부
        /// </summary>
        /// <returns>동의 여부</returns>
        public bool IsAuthorizationTracking()
        {
#if UNITY_IOS
            if (Application.platform == RuntimePlatform.IPhonePlayer)
            {
                var status = Unity.Advertisement.IosSupport.ATTrackingStatusBinding.GetAuthorizationTrackingStatus();
                Debug.LogWarningFormat("[AuthorizationTrackingStatus] {0}", status);

                switch(status)
                {
                    case Unity.Advertisement.IosSupport.ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED: // 결정되지 않음
                    case Unity.Advertisement.IosSupport.ATTrackingStatusBinding.AuthorizationTrackingStatus.RESTRICTED:     // 액세스 권한이 제한된 상태
                    case Unity.Advertisement.IosSupport.ATTrackingStatusBinding.AuthorizationTrackingStatus.DENIED:         // 거부됨
                        return false;

                    case Unity.Advertisement.IosSupport.ATTrackingStatusBinding.AuthorizationTrackingStatus.AUTHORIZED:     // 동의함
                        return true;
                }
            }
#endif
            return false;
        }
        #endregion

 

 

[CurrentGDPR.cs]

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System.Linq;
using GoogleMobileAds.Api;

public static class CurrentGDPR
{
    private static bool _isGdprOn;
    private static string _purposeConsent, _vendorConsent, _vendorLi, _purposeLi, _partnerConsent;

    static CurrentGDPR()
    {
        // 원하는 시점에 Init하도록 변경[blueasa / 2024-03-21]
        //InitData();
    }

    public static void InitData()
    {
        int gdprNum = 0;

        // IABTCF_* 관련 항목 설명
        // [참조] https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details

        // GoogleMobileAds 8.7.0에서 ApplicationPreferences 추가됨
        // [참고] https://stackoverflow.com/questions/77838024/admob-gdpr-ump-issue-empty-iab-tcf-strings-on-android-after-user-consent
        // [참고] https://developers.google.com/admob/android/privacy/gdpr?hl=ko
        gdprNum = ApplicationPreferences.GetInt("IABTCF_gdprApplies");
        _purposeConsent = ApplicationPreferences.GetString("IABTCF_PurposeConsents");
        _vendorConsent = ApplicationPreferences.GetString("IABTCF_VendorConsents");
        _vendorLi = ApplicationPreferences.GetString("IABTCF_VendorLegitimateInterests");
        _purposeLi = ApplicationPreferences.GetString("IABTCF_PurposeLegitimateInterests");
        _partnerConsent = ApplicationPreferences.GetString("IABTCF_AddtlConsent");

        #region GoogleMobileAds 8.6.0 이전 버전
//#if UNITY_EDITOR // 에디터에서는 자바 호출이 에러나서 에외처리
//        gdprNum = 1;
//        _purposeConsent = "0000000000";
//        _vendorConsent = "0000000000";
//        _vendorLi = "";
//        _purposeLi = "";
//        _partnerConsent = "";
//#elif UNITY_ANDROID
//        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
//        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
//        AndroidJavaClass preferenceManagerClass = new AndroidJavaClass("android.preference.PreferenceManager");
//        AndroidJavaObject sharedPreferences = 
//                 preferenceManagerClass.CallStatic<AndroidJavaObject>("getDefaultSharedPreferences", currentActivity);

//        gdprNum = sharedPreferences.Call<int>("getInt", "IABTCF_gdprApplies", 0);
//        _purposeConsent = sharedPreferences.Call<string>("getString", "IABTCF_PurposeConsents", "");
//        _vendorConsent = sharedPreferences.Call<string>("getString", "IABTCF_VendorConsents", "");
//        _vendorLi = sharedPreferences.Call<string>("getString", "IABTCF_VendorLegitimateInterests", "");
//        _purposeLi = sharedPreferences.Call<string>("getString", "IABTCF_PurposeLegitimateInterests", "");
//        _partnerConsent = sharedPreferences.Call<string>("getString", "IABTCF_AddtlConsent", "");
//#elif UNITY_IOS
//        gdprNum = PlayerPrefs.GetInt("IABTCF_gdprApplies", 0);
//        _purposeConsent = PlayerPrefs.GetString("IABTCF_PurposeConsents", "");
//        _vendorConsent = PlayerPrefs.GetString("IABTCF_VendorConsents", "");
//        _vendorLi = PlayerPrefs.GetString("IABTCF_VendorLegitimateInterests", "");
//        _purposeLi = PlayerPrefs.GetString("IABTCF_PurposeLegitimateInterests", "");
//        _partnerConsent = PlayerPrefs.GetString("IABTCF_AddtlConsent", "");
//#endif
        #endregion

        // 0 이면 아예 GDPR 대상이 아님. 1이어야 GDPR
        if (gdprNum == 1)
            _isGdprOn = true;
        else
            _isGdprOn = false;

        Debug.Log("GDPR을 띄우는가? " + _isGdprOn);
        Debug.Log("광고에 필요한 권한 동의: " + _purposeConsent);
        Debug.Log("광고에 필요한 적법관심(광고 개인화?) 동의: " + _vendorConsent);
        Debug.Log("구글에 동의처리가 되어있는가?: " + _vendorLi);
        Debug.Log("구글에 적법관심(광고 개인화?) 처리 여부: " + _purposeLi);
        Debug.Log("파트너 네트워크 여부: " + _partnerConsent);
    }

    /// <summary>
    /// GDPR을 띄워야 할 유저인지(EEA = 유럽 + 영국) 리턴
    /// </summary>
    /// <returns>EEA 여부</returns>
    public static bool IsGDPR()
    {
        return _isGdprOn;
    }

    /// <summary>
    /// 광고에 필요한 권한 동의 여부
    /// </summary>
    /// <returns>동의 여부</returns>
    public static bool IsPurposeConsents()
    {
        // Example value:
        // 동의   : "1111111111"
        // 비동의 : "0"

        if (true == string.IsNullOrEmpty(_purposeConsent))
        {
            return false;
        }

        Debug.LogFormat("[IsPurposeConsents] {0}", HasAttribute(_purposeConsent, 1));

        // Purposes are zero-indexed. Index 0 contains information about Purpose 1.
        // _purposeConsent[0]에 1(동의)이 있는지 체크
        return HasAttribute(_purposeConsent, 1);
    }

    /// <summary>
    /// 광고에 필요한 적법관심(광고 개인화?) 동의 여부
    /// </summary>
    /// <returns>동의 여부</returns>
    public static bool IsVendorConsents()
    {
        if (true == string.IsNullOrEmpty(_vendorConsent))
        {
            return false;
        }

        // _vendorConsent[0]에 1(동의)이 있는지 체크
        return HasAttribute(_vendorConsent, 1);
    }

    // 광고가 보여지는지 여부 리턴
    public static bool CanAdShow()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        // 광고 가능 - 비개인화 광고
        // return HasConsentFor(new List<int> { 1 }, _purposeConsent, hasGoogleVendorConsent)
        //        && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 }, 
        //            _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);

        // 광고 가능 - 제한적인 광고 - 1에 대한 권한이 없어도 됨 ㅇㅇ
        return HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 },
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    // 개인화 광고가 보여지는지 여부 리턴
    public static bool CanShowPersonalizedAds()
    {
        int googleId = 755;
        bool hasGoogleVendorConsent = HasAttribute(_vendorConsent, googleId);
        bool hasGoogleVendorLi = HasAttribute(_vendorLi, googleId);

        return HasConsentFor(new List<int> { 1, 3, 4 }, _purposeConsent, hasGoogleVendorConsent)
               && HasConsentOrLegitimateInterestFor(new List<int> { 2, 7, 9, 10 },
                   _purposeConsent, _purposeLi, hasGoogleVendorConsent, hasGoogleVendorLi);
    }

    public static bool IsPartnerConsent(string partnerID) // 파트너 권한 있는지 확인
    {
        return _partnerConsent.Contains(partnerID);
    }

    // 이진 문자열의 "index" 위치에 "1"이 있는지 확인합니다(1 기반).
    private static bool HasAttribute(string input, int index)
    {
        // null 예외처리[blueasa / 2024-03-21]
        if(true == string.IsNullOrEmpty(input) || (input.Length <= index))
            return false;

        return (input[index - 1] == '1');
    }

    // 목적 목록에 대한 동의가 주어졌는지 확인합니다.
    private static bool HasConsentFor(List<int> purposes, string purposeConsent, bool hasVendorConsent)
    {
        return purposes.All(p => HasAttribute(purposeConsent, p)) && hasVendorConsent;
    }

    // 목적 목록에 대한 공급자의 동의 또는 정당한 이익이 있는지 확인합니다.
    private static bool HasConsentOrLegitimateInterestFor(List<int> purposes, string purposeConsent, string purposeLI, bool hasVendorConsent, bool hasVendorLI)
    {
        return purposes.All(p =>
            (HasAttribute(purposeLI, p) && hasVendorLI) ||
            (HasAttribute(purposeConsent, p) && hasVendorConsent));
    }
}

 

 

[참조] https://docs.adxcorp.kr/appendix/ump-user-messaging-platform

 

UMP (User Messaging Platform) - ADX Library

IDFA 메시지 작성은 선택사항이지만, GDPR 메시지 사용 설정을 할 경우, IDFA 메시지 작성도 같이 작성하십시오. 애드몹 UMP의 GDPR 동의 화면이 보이는 상태에서, 프로그래밍 방식으로 수동으로 ATT (AP

docs.adxcorp.kr

[사용자 메시지 플랫폼(UMP) Doc] https://developers.google.com/admob/unity/privacy?hl=ko

 

시작하기  |  Unity  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Google EU 사용자 동의 정책에 따

developers.google.com

[GoogleMobileAds.Ump.Api Doc] https://developers.google.com/admob/unity/reference/namespace/google-mobile-ads/ump/api

 

GoogleMobileAds.Ump.Api Namespace  |  Unity  |  Google for Developers

Stay organized with collections Save and categorize content based on your preferences. GoogleMobileAds.Ump.Api Summary Enumerations ConsentStatus ConsentStatus Consent status values. Properties NotRequired Consent not required. Obtained User consent obtain

developers.google.com

[GitHub/Sample] https://github.com/googleads/googleads-mobile-unity

 

GitHub - googleads/googleads-mobile-unity: Official Unity Plugin for the Google Mobile Ads SDK

Official Unity Plugin for the Google Mobile Ads SDK - googleads/googleads-mobile-unity

github.com

반응형
Posted by blueasa
, |

[추가] 2021-08-05 확인

GoogleMobileAds v6.0.1(2021-06-27 업데이트)에서 수정됐다.

https://github.com/googleads/googleads-mobile-unity/releases/tag/v6.0.1

------------------------------------------------------------------------

 

GoogleMobileAds v6.0.0이 나와서 설치했는데 iOS에서 Build Error가 나서 확인해보니 GoogleMobileAds v6.0.0 버그 같다.

 

아래와 같이 Workaround.m 파일을 만들어서 추가하면 해결 된다.

------------------------------------------------------------------------

qbit86 commented 2 days ago

@onom88
It would probably be sufficient to reduce Assets/Plugins/iOS/Workaround.m to just this, with no headers needed at all:
// Workaround for https://github.com/googleads/googleads-mobile-unity/issues/1616

typedef const void *GADUTypeInterstitialRef;

typedef const void *GADUTypeRewardedAdRef;

typedef const void *GADUTypeRequestRef;

void GADURequestInterstitial(GADUTypeInterstitialRef interstitial, GADUTypeRequestRef request) { }

void GADURequestRewardedAd(GADUTypeRewardedAdRef rewardedAd, GADUTypeRequestRef request) { }

 

 

[링크] https://github.com/googleads/googleads-mobile-unity/issues/1616

 

Build iOS failed: Undefined symbols for architecture arm64: "_GADURequestInterstitial" · Issue #1616 · googleads/googleads-mob

Unity version: 2020.3.1 - 2020.3.6 Google Mobile Ads Unity plugin version: 6.0.0 Platform: iOS Platform OS version: n/a Any specific devices issue occurs on: n/a Mediation ad networks used, and the...

github.com

 

반응형
Posted by blueasa
, |


[링크] https://assetstore.unity.com/packages/tools/integration/very-simple-ads-monetization-mediation-57517

반응형
Posted by blueasa
, |


[링크] http://minhyeokism.tistory.com/69



반응형
Posted by blueasa
, |

■iOSの広告識別子「IDFA=Advertising Identifier」

■Androidの広告ID「ADID=AdvertisingID」

■実機操作による確認方法
 ・Android
  「設定」->「Google」->「広告」で広告IDを確認

 ・iPhone
  わかんない

●Unity公式のサンプルスクリプト
https://docs.unity3d.com/jp/540/ScriptReference/Application.RequestAdvertisingIdentifierAsync.html

advertisingId には広告ID
enabledにはユーザーが広告IDを使用させるか?の有無が返ってくる

■自分用にソース

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GetIdfaAndAdid : MonoBehaviour {

    [SerializeField]
    public Text DisplayID;

    // ボタンが押されると呼ばれる
    public void GetID()
    {
        Debug.Log("■■■■■GetID()■■■■■");
        // IDFA
        Application.RequestAdvertisingIdentifierAsync(
                (string advertisingId, bool trackingEnabled, string error) =>
                {
                    Debug.Log("advertisingId=" + advertisingId + " enabled=" + trackingEnabled + " error=" + error);
                    DisplayID.text = "advertisingId=" + advertisingId + " enabled=" + trackingEnabled + " error=" + error;

                }
        );
    }
}

■参考URL
https://reviewlog.info/adid-android/
https://reviewlog.info/ios-idfa-reset/
https://stackoverflow.com/questions/34193282/apple-idfa-in-unity-via-plugin

Appleの規約
https://support.apple.com/ja-jp/HT205223



[출처] https://qiita.com/satotin/items/f4afb0a08adbec26a062

[참조] https://docs.unity3d.com/ScriptReference/Application.RequestAdvertisingIdentifierAsync.html

반응형
Posted by blueasa
, |

UnityAds에는 보상형광고와 일반형광고 두가지가 있습니다.

일반형과 보상형의 차이는 스킵버튼이 활성화되어 사용자가 광고를 스킵할수 있는지 없는지의 차이 입니다.

일반형으로 설정시(아무런 설정하지 않으면 일반형이 기본값입니다) 사용자는 5초후 광고 스킵 버튼이 활성화 되어5초후 부터 광고를 스킵할수 있습니다.
(5초 이외에 원하는 시간으로 설정 가능)

하지만 보상형으로 설정시 스킵버튼이 나오지 않아 사용자는 무조건 광고를 끝까지 보아야 합니다.사용자가 무조건 광고를 끝까지 보게 되므로 수익을 얻을 가능성이 좀 더 높일 수 있습니다.

따라서 보상형 광고로 설정하는 것을 추천 드립니다.

게임을 추가하여 게임ID가 생성되면, 하기와 같이, 해당 게임ID에 기본적으로 일반형광고 ID와 보상형 광고 ID가 2가지가 추가되어 있습니다.
(PictureZone은 현재 지원하지 않는 기능입니다)

스크린샷 2015-07-21 오후 1.28.27

이 아이디를 사용하여 보상형 광고로 설정할 수 있습니다.

보상형 비디오로 설정하는 방법 가이드 입니다.

1. Code examples for Unity Ads package

Show 의 Parameter로 zoneid를 주면 됩니다.

Advertisement.Show(“rewardedVideoZone”);

만약 일반형광고를 원한다면, 다음과 같이 zoneid를 설정하지 않거나 null을 넘기면 일반형 광고로 설정됩니다.

Advertisement.Show();

2. Code examples for native iOS

IOS NATIVE SDK로 할때는, A) 항상 기본형을 사용하거나 B) 항상 show를 보여주기 전에 setZone(“”)을 합니다.

if ([[UnityAds sharedInstance] canShow]) {   
               [[UnityAds sharedInstance] setZone:@”rewardedVideoZone”];
        [[UnityAds sharedInstance] show:….];

}

3. Code examples for Android

Andorid NATIVE SDK로 할때는, A) 항상 기본형을 사용하거나 B) 항상 show를 보여주기 전에 setZone(“”)을 합니다.

if(UnityAds.canShow()){   
   UnityAds.setZone(“rewardedVideoZone”);   
   UnityAds.show(options); 
}

만약 광고를 보여주는 곳 마다 ad placement id를 다르게 설정하여 광고별로 성과를 알고 싶다면,하기와 같이 ad placement id를 생성한 후 사용하면됩니다.
unityads.unity3d.com/admin 페이지의에서   게임 > 게임 선택 > 수익화 설정> 고급설정 광고 게재 위치 옆 +새 광고 게재 위치 추가 버튼 클릭

+새광고 위치 추가 버튼을 클릭하면 다음화면이 나옵니다.

스크린샷 2015-07-21 오전 10.07.54

보상형 광고로 선택합니다.
보상형 광고로 선택하게 되면 사용자가 광고를 스킵할 수 없습니다. 혹시 추가적으로 원하시는 옵션이 있으시면 선택후 저장하시면 됩니다.

저장 후 다음과 같이 추가된 것을 확인 할 수 있습니다.

rewardedvideozone

그리고 개발하시는 게임의 코드상에서 해당 ZONE ID를 사용하시면 됩니다
즉 위의 캡쳐화면의 예제에서는 “199981428975268” 를 사용하시면 됩니다.

Advertisement.Show(“199981428975268″);

또한 참고로, 현재 인터넷 익스플로러에서 추가된 zone id 목록이 보이지 않는 경우가 있습니다.
따라서 zoneid를 추가하였는데도 목록에서 보이지 않는 경우, 크롬으로 확인해주세요.



[출처] http://www.unityads.co.kr/?p=1285

반응형

'Unity3D > Ad' 카테고리의 다른 글

[링크] [Unity3D] Google AdMob 연동하기  (0) 2018.01.08
Unity에서 ADID・IDFA 얻기  (0) 2017.11.28
[Unity] Vungle Plugin 설치  (0) 2016.07.26
[Unity] Google AdMob Plugin 설치하기  (0) 2016.07.26
Unity Ads 연동  (0) 2016.07.26
Posted by blueasa
, |


[링크] http://evir.tistory.com/entry/Unity-Vungle-Plugin-%EC%84%A4%EC%B9%98

반응형
Posted by blueasa
, |


[링크] http://evir.tistory.com/entry/Unity-Google-AdMob-Plugin-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0

반응형
Posted by blueasa
, |