본문 바로가기
  • Survival Plan
개발 이야기

애드몹(adMob) 테스트 디바이스 (testDevice) 등록

by IT/머신러닝 엔지니어의 독서/경제/육아 공부 리치윈드 - windFlex 2020. 4. 22.

최근에 AdSense / AdMob의 무효트래픽 관련 건으로, adSense 계정이 정지 당한 사건이 겪었습니다. 

확인 결과 AdSense 쪽 문제는 아니었고, AdMob 계정이 원인이었는데, 

AdSense와 AdMob계정이 하나로 묶여있어서 덩달아서 계정이 정지 당했던 건이었습니다. 

              * 관련 포스팅 : https://windflex.tistory.com/90

 

AdMob은 테스트 광고를 실행하고, 앱을 출시하기 직전에 실제 광고로 변경해 주어야 하는 구조로 이루어져 있습니다.

이 과정에서, 최종적으로 광고가 동작하는지 확인하게 되는데, 이걸 몇번만 하면 바로 무효트래픽으로 간주 됩니다. 

( * AdMob은 모바일앱에 광고를 출력해 주는 플랫폼 입니다.)

 

문제는 AdMob은 개발은 테스트하는 과정에서, 이러한 Case의 재발이 아주 빈번하게 발생할 수 있다는 것입니다.

다시 동일한 경우가 발생하면, 영구정지 된다는 협박아닌 협박을 받았기 때문에, 이 요소가 민감하게 다가 왔으며, 

근본적인 문제 해결이 필요했습니다. 

 

근본적인 방법으로는, 제가 사용하는 모바일 기기는 전부 TEST처리 하는 방법입니다. 

 

JAVA로 안드로이드 개발할 때는 addTestDevice() 함수를 사용하여,

기기 자체를 TEST용 기기로 등록해 놓고 테스트를 진행하는 방법을 사용할 수 있습니다. 

React-Native로 개발하는 경우는, 이 것에  관련된 문서가 매우 부족하더군요.

 


아래는, JAVA로 안드로이드 개발하는 경우에 TestDevice를 등록과정의 일부 입니다. 

 if(YourApplication.debugEnabled(this)) //debug flag from somewhere that you set
    {

        String android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
        String deviceId = md5(android_id).toUpperCase();
        mAdRequest.addTestDevice(deviceId);
        boolean isTestDevice = mAdRequest.isTestDevice(this);

        Log.v(TAG, "is Admob Test Device ? "+deviceId+" "+isTestDevice); //to confirm it worked
    }

위 코드에서 확인 할 수 있듯이,

addTestDevice( "디바이스 아이디 문자열" );

형태로 입력해 주면 됩니다. 

 


그러나, react-native의 경우에는 어떻게 할 수 있을까? ...... 

react-native x AdMob

 

@react-native-firebase/admob v5에서는, JAVA 파일에서 직접 위 코드처럼 MainApplication.java를 수정하면 됩니다. 

따라서, react-native와는 무관하게 JAVA 개발자와 동일한 방법으로 수정하면 됩니다. 

 

그런데, 문제는 이게 react-native-firebase/admob의 v5 이하에서만 가능하다는 것입니다. 

불행하게도 저는 최신 버전인 v6를 사용하고 있습니다..... v5 사용할걸 그랬습니다. ㅜ_ㅜ 

 

최신버전인 v6로 admob을 사용하는 개발자가 아직은 많지 않은것 같습니다. ㅜ_ㅜ

(이래서 안정화 버전을 사용해야 하는데,,, 선발대가 되어 버렸네요. ^^;;;)

 

v6 버전에서는 JAVA 파일에 adRequest라는 인스턴스 자체를 사용하지 않도록 변경 되었습니다. 

물론, Java 파일에서 라이브러리의 위치를 잘 찾아서, import 하고 사용하면 되겠지만, 

계속 이렇게 의존하면 끝도없이 Java 파일을 건드리게 되고, 그렇게 되면 react-native로 개발하는 의미가 없을 것 같습니다. 

 

그래서, Java 파일은 손대지 않는 방향으로 진행했습니다.  

 

react-native-firebase/admob Reference 문서를 전발적으로 살펴 보았습니다. 

결론적으로, 두개 영역에서 TestDevice를 설정하는 부분을 발견 되더군요.


방법 1) requestOptions

 

광고유형별 Component들 (즉, BannerAd, InterstitialAd, RewardAd 등)은 각 인스턴스를 생성할 때,

requestOptions를 설정 할 수 있도록 매개변수를 가지고 있습니다. 

 

다음은 interstitialAd의 생성 예 입니다.

import { InterstitialAd, TestIds } from '@react-native-firebase/admob';

const interstitial = InterstitialAd.createForAdRequest(TestIds.INTERSTITIAL, {
    requestNonPersonalizedAdsOnly: true,
});

createForAdRequest Method는 2번째 인자로 requestOptions를 입력 받고 있으며, requestOptions에 testDevices 속성이 있다. testDevices 속성에 DeviceId를 입력함으로써 test device를 지정할 수 있습니다.

더보기
createForAdRequest(adUnitId: string, requestOptions?: RequestOptions): InterstitialAd;

 

requestOptions properties

참조 : https://rnfirebase.io/reference/admob/requestoptions


방법 2) AdsConsent 의 method 중에, addTestDevices  method

(* Java에서의 method명과 다르다. JAVA는 addTestDevice인데, react-native의 method명은 복수인 addTestDevices 이다)

 

 

addTestDevice 와 addTestDevices 는 인자값이 다릅니다.

JAVA의 addTestDevice는 device Id의 문자열 값을 입력 받고 있습니다.

위 react-native addTestDevices API 문서에도 표기되어 있지만, 문자열값을 인자로 넣으면 Error가 발생합니다.

문자열의 배열로 넣어야 한다. 이래서 복수형태인 addTestDevices 표기를 한것인데요.

이런것을 볼 때 영어문화권 사람들이 참 꼼꼼하다는 생각이 든다. 


최종적으로, 

React-native 에서는 2가지 형태로 TestDevices를 지정할 수 있습니다.

    1) requestOptions

    2) addTestDevices 메소드

그런데, requestOptions를 사용하면, 생성 시점에 이미 지정을 했기 때문에, 굳이 addTestDevices를 사용할 필요가 없습니다.


다음은 제가 사용하는 requestOptions 사용의 예이다. 

<BannerAd
        unitId={myAdBannerUnit}
        size={BannerAdSize.FULL_BANNER}
        requestOptions={{
          requestNonPersonalizedAdsOnly: true,
          testDevices:["EMULATOR", "75b159xxxxxx6fcdxxxxxxd74fxxxx93".toUpperCase()],
        }}
        onAdLoaded={() => {
          console.log('BANNER loaded');
        }}
        onAdFailedToLoad={error => {
          console.warn('BANNER_AD failed to load: ', error);
        }}
      />

TestDevices ID를 입력할 때, 모두 대문자로 입력해 주어야 하는 점에 유의하도록 하자.

이러한 이유로, toUpperCase() 함수를 붙여 주었습니다.


자... 이렇게 하면 특정 단말을 Test Device로 등록해 둘 수 있습니다.

다시 말하면, 무효화 트래픽으로 AdMob 계정정지 먹는것을 방지하며, 자유롭게 테스트 할 수 있다는 이야기다. 

그런데, 여기서 중요하고 난해한 점이 하나 있다. 

Device ID는 어떤 값을 넣어야 하는가?

으잉 ?  ^^;;;;;;;;;;;

Google AdMob SDK 가이드....."테스트 기기 ID를 복사합니다" ^^;;;;

 

Android에는 다양한 ID 값들이 존재합니다.. 

Google 개발자 커뮤니티를 참조하면, IMEI 값 등 Physical ID 값을 사용하면 안된다고 가이드 하고 있습니다.

따라서, 소프트웨어적으로 혹은 랜덤하게 생성해낸 값들이 다수 존재합니다.

이러한 값들이 모두 xxxx ID,  Device ID 라는 이름으로 혼용되어 사용되고 있습니다.

 

이중 대표적인 것으로, Advertising Android ID,Unique ID, Device ID, Android ID 등이 있습니다.

각각은 고정된 값을 유지하는 범주가 다릅니다.

 

무슨말이냐면, 

 - Advertising Android ID (이하 AAID)는 안드로이드 기기 마다  고유한 값 (기기를 구분할 수 있다.)

 - Unique ID, Android ID는 (기기 + App)의 고유한 값 : 동일한 기기라도 App가 다르면 다른 값이라는 것입니다. 

더보기

Unique ID, Android ID

*좀더 정확하게는, signing key가 다르면 다른값을 같게 된다. 

* Reference 문서에 따르면 Signing Key, user, Device ID의 값으로 정의된다고 한다. (아마도 Salt + Hashing)


결론적으로, 위 ID 들 중에서 TestDevice 등록에 사용되는 ID는 android ID 입니다.

 - 안드로이드에서는 Unique ID를 호출하면, android ID 로 Bypass 한다. (unique ID는 ios에서 의미 있는 값이다. )

더보기

Test Device 등록에 AAID를 사용하지 않고, 왜 App 마다 변하는 Android ID를 사용하느냐 하는 점인데,아마도, AAID는 Device를 지칭할 수 있기 때문에, 개인정보에 대한 민감성을 배제하기 위한 것이 아닐까 생각해 보네요.

 

여하튼, AAID를 사용하지 않고 Android ID를 사용해서, TestDevice를 지정하기 때문에,

이 testDevice ID를 미리 확보해 둘 수가 없습니다.

 

동일한 signing key를 사용한다면 개인마다 고정된 값으로 사용할 수는 있으나, 

 

이러게 주저리 주저리 설명하는 이유가 무엇인고 하니, 타인에게 Device ID를 보여주는 툴을 제공할 수가 없습니다.

즉, 자기자신이 직접 확인해야 한다는 말이죠. ==> 제가 당신의 Device ID를 확인해 줄 수 없다는 이야기 입니다. 

(확인하는 앱을 만들 수 있으나, Signing Key가 필요합니다. Signing Key를 공유하진 않겠죠?)

 

더보기

 

Android Native 개발 (JAVA) 개발 할 때에도,

Device ID 확인을 위해서, 특정 Field 값이 있는 것이 아닌,

Android Studio를 일단 실행하고, Logcat으로 Log Event에 있는 문자열 값을 확인하는 방식으로 진행 된다. 

 

왜 이렇게 불편하게 할까..... 라고 생각 했었는데, 상기에서 설명한 변경되는 값을 가지고 있기 때문이다. 

 

저와 같은 시행착오를 겪지 않도록, Device ID 확인 앱을 만들어서 올려 놓으려 했는데, 어렵네요.^^;;

Play Store에 검색해 보면, 다음과 같은 Device ID 확인 앱들이 있습니다. (미리 말씀 드리면, 아~~~무 의미 없습니다.)

 

그 중에서 하나를 확대해 보면, Android Device ID 값을 보여주는 어플이다. 

 

이거....

이런거 믿고 개발하면서, 왜 동일한 값이 나오지 않아서 한참 헤맸습니다.. 에고 ^^;;;;;

 

저 개발자는 본인의 코드상 andorid Device ID가 정확하게 출력 되었을 것인데,

앞서 설명했지만, Android Device ID를 출력해 주더라도 아무런 의미가 없습니다. 

App Signing Key에 따라서 값이 달라지기 때문에, 해당하는 App에만 의미가 있는거죠.

(혹시 저 App 개발자가 본인의 Signing Key를 공유해 준다면 또 다른 이야기 이겠지만 ㅎㅎ)

 

쟤들 개발자들은 이거 모르거 올려놓은 것인지? 아니면, 알고도 올려 놓은 것인지 모르겠다. 사기꾼인가?

 

다시 결론을 말하면, 저런 앱들 도움을 받을 수 없고, 

본인의 App에서 본인의 Android ID를 확인하고, 다음 2개 단계를 거쳐야 합니다.

   1) signing Key를 먼저 확정 한다. (나중에 signing key 변경하면 Android ID도 변경되므로..)

   2) Andorid Id 값을 확인한다. 

   3) 그리고 그 다음으로, testDevices 에 등록한다. 

 * 본인이 개발하는 app의 Signing key를 동일하게 유지 한다면, 1회 확인 후 계속 사용이 가능하다 하겠다. 

 


마지막으로, react-native에서 admob test Device 등록을 위한 패키지 설치와 코드를 보시죠.

 

1) 필요 패키지를 설치하자.

   ( android ID를 이미 알고 있다면, 3단계로 건너 뛰어 진행한다. )

$ yarn add react-native-device-info react-native-md5

간혹 yarn으로 설치할 경우 react-native-device-info 가 제대로 link 되지 않는 경우가 있다. 이 경우에는 npm을 이용한다. 

npm install react-native-device-info --save
npm link react-native-device-info 
더보기

추후 test Device 값은 android ID의 md5 hash 값으로 입력하기 때문에 md5 라이브러리를 추가 하였다. 그러나, 다음과 같은 사이트에서 수작업으로 확인 할 수도 있다. 

https://coding.tools/kr/md5

 

2) android ID를 확인한다. 

 

적당한 곳에 andorid ID를 확인하를 루틴을 넣는다. 

1) ComponentDidMount() lifecycle, 2) Button의 onPress의 callback

필자는 state값에 반영하는 setAndroidId를 사용하였지만, 각자 취향에 맞게 확인 하면 된다.

DeviceInfo.getAndroidId().then(androidId => {
    const md5id = md5.hex_md5(androidId);
    //setAndroidId(md5id);
    console.log(' - android Id' + androidId); //==> 숫자
    console.log(' - md5:' + md5id);      //==> md5Hash
    console.log(' - md5:' + md5id.toUpperCase());      //==> Hash 대문자
  });

 

3) testDevices 를 등록한다. 

단계 2에서 확인한 Hash 문자열을 사용하여, testDevices 필드에 array로 설정한다. 

<BannerAd
        unitId={myAdBannerUnit}
        size={BannerAdSize.FULL_BANNER}
        requestOptions={{
          requestNonPersonalizedAdsOnly: true,
          testDevices:["EMULATOR", "75b159xxxxxx6fcdxxxxxxd74fxxxx93".toUpperCase()],
        }}
        onAdLoaded={() => {
          console.log('BANNER loaded');
        }}
        onAdFailedToLoad={error => {
          console.warn('BANNER_AD failed to load: ', error);
        }}
      />

 

expo를 사용하여 개발하는 경우, 메소드가 다릅니다. 이점 참조 바랍니다. 

더보기
import {
  AdMobBanner,
  AdMobInterstitial,
  PublisherBanner,
  AdMobRewarded
} from 'expo-ads-admob';

// Display a banner
<AdMobBanner
  bannerSize="fullBanner"
  adUnitID="ca-app-pub-3940256099942544/6300978111" // Test ID, Replace with your-admob-unit-id
  testDeviceID="EMULATOR"
  servePersonalizedAds // true or false
  onDidFailToReceiveAdWithError={this.bannerError} />

// Display a DFP Publisher banner
<PublisherBanner
  bannerSize="fullBanner"
  adUnitID="ca-app-pub-3940256099942544/6300978111" // Test ID, Replace with your-admob-unit-id
  testDeviceID="EMULATOR"
  onDidFailToReceiveAdWithError={this.bannerError}
  onAdMobDispatchAppEvent={this.adMobEvent} />

// Display an interstitial
AdMobInterstitial.setAdUnitID('ca-app-pub-3940256099942544/1033173712'); // Test ID, Replace with your-admob-unit-id
AdMobInterstitial.setTestDeviceID('EMULATOR');
await AdMobInterstitial.requestAdAsync({ servePersonalizedAds: true});
await AdMobInterstitial.showAdAsync();

// Display a rewarded ad
AdMobRewarded.setAdUnitID('ca-app-pub-3940256099942544/5224354917'); // Test ID, Replace with your-admob-unit-id
AdMobRewarded.setTestDeviceID('EMULATOR');
await AdMobRewarded.requestAdAsync();
await AdMobRewarded.showAdAsync();

에고 길고 긴 글을 읽어 주셔서 감사 합니다. 

댓글1

  • HeroKim 2020.06.13 13:25

    안녕하세요. 좋은 내용 정리 잘 해주셔서 감사드립니다.

    같은 문제를 겪고, Option을 이용한 testDevices 등록을하고있는데요.

    이렇게 테스트디바이스 등록을 하고,
    unitId 를 테스트가 아닌, 광고용 자신의 애드몹 아이디를 입력하고
    실행시키면

    unitId를 광고테스트 아이디로 했을 경우랑 같은 테스트 광고 결과가 나와야 하나요 ??ㅎㅎ

    이미 광고 게제 제한이 걸려서 광고란이 빈 화면으로 출력되는데,
    올려주신대로 따라해도 그냥 빈 화면으로 출력되는게 같아서요.

    이미 광고 게제 제한이라 요청이 실패해서 테스트광고화면도 안뜨는건지
    아님 제대로 적용을 못한건지 알수가 없어서요 ㅎㅎ

    긴글 읽어주셔서 감사드립니다.
    바쁘시겠지만 한두줄이라도 코멘트를 달아주시면 감사하겠습니다!



    답글