1) npm 모듈
https://www.npmjs.com/package/@react-native-firebase/admob2) 관련 issues
https://github.com/invertase/react-native-firebase/issues/51793) 공식 문서: Android Native UI Components
https://reactnative.dev/docs/0.65/native-components-android4) patch 파일 만들기
https://github.com/ds300/patch-package
앱을 처음 출시한 지 약 1년 반 만에 드디어 애드몹 광고를 달았다.
광고를 늦게 단 이유는
막상 광고를 달려는데, 애정이 듬뿍 담긴 내 첫 앱이라 그런지 광고로 앱을 더럽히고 싶지는 않았다. 그래서 유저들이 사용할 때 가장 영향을 덜 받을만한 페이지에 배너 광고를 두 개 넣는 것으로 시작했다.
(자기계발 겸 부수입을 얻는 것이 목표이지만...)
그렇게 나름 힘들게 광고를 달았는데, 앱 실행 후 첫 광고는 항상 뜨지 않았다. 사실 테스트 할 때부터 알고 있던 문제 현상이었는데, 먼저 업데이트하는 것이 우선이었기 때문에 일단 구글 플레이 스토어에 올려버렸다.
[ 요약 ]
- 배너 광고가 달린 페이지에서 앱 실행 후 처음 요청하는 광고는 항상 불러오지 못한다. 좀 더 자세히 보니, 광고를 아예 요청하지 못한다.
- 두 번째 요청부터는 문제없이 동작한다.
Reference 링크의 관련 issues가 큰 힌트가 되었다.
npm 모듈이라고 항상 완벽한 법은 없다. 종종 고쳐야 하는 경우가 생긴다.
분석을 위해, 공식 문서를 참고하여 코드를 익혔다.
1) 파일 경로
root폴더\node_modules\@react-native-firebase\admob\android\src\main\java\io\invertase\firebase\admob\ReactNativeFirebaseAdMobBannerAdViewManager.java2) 디버깅을 위해 +코드를 추가
size, unitId, request 중 하나 이상 null이기 때문에 광고 요청을 하지 못하고 return 되는 것으로 추정되었다.package io.invertase.firebase.admob; ... import java.util.Map; + import android.util.Log; ... @ReactProp(name = "unitId") public void setUnitId(ReactViewGroup reactViewGroup, String value) { + Log.d("TEST_ADMOB", "setUnitId"); unitId = value; } @ReactProp(name = "request") public void setRequest(ReactViewGroup reactViewGroup, ReadableMap value) { + Log.d("TEST_ADMOB", "setRequest"); request = ReactNativeFirebaseAdMobCommon.buildAdRequest(value); //requestAd(reactViewGroup); } @ReactProp(name = "size") public void setSize(ReactViewGroup reactViewGroup, String value) { + Log.d("TEST_ADMOB", "setSize"); size = ReactNativeFirebaseAdMobCommon.getAdSize(value, reactViewGroup); int width; int height; WritableMap payload = Arguments.createMap(); if (size == AdSize.SMART_BANNER) { width = (int) PixelUtil.toDIPFromPixel(size.getWidthInPixels(reactViewGroup.getContext())); height = (int) PixelUtil.toDIPFromPixel(size.getHeightInPixels(reactViewGroup.getContext())); } else { width = size.getWidth(); height = size.getHeight(); } payload.putDouble("width", width); payload.putDouble("height", height); if (size != AdSize.FLUID) { sendEvent(reactViewGroup, "onSizeChange", payload); } requestAd(reactViewGroup); } ... private void requestAd(ReactViewGroup reactViewGroup) { + Log.d("TEST_ADMOB", ""+size); + Log.d("TEST_ADMOB", ""+unitId); + Log.d("TEST_ADMOB", ""+request); if (size == null || unitId == null || request == null) { return; } if (requested) { resetAdView(reactViewGroup); } AdView adView = getAdView(reactViewGroup); adView.setAdUnitId(unitId); adView.setAdSize(size); adView.loadAd(request); requested = true; } ...
3) 디버깅 결과 (Android Studio의 Logcat TEST_ADMOB filtering)
2022-01-30 22:09:40.829 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: setSize 2022-01-30 22:09:40.830 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: 320x100_as 2022-01-30 22:09:40.830 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: null 2022-01-30 22:09:40.830 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: null 2022-01-30 22:09:40.830 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: setUnitId 2022-01-30 22:09:40.830 28333-28333/com.howwwwwhy.heartwarming D/TEST_ADMOB: setRequest
setSize에서 requestAd 함수를 호출한 후에 setUnitId와 setRequest가 호출되는 것이 원인인 듯 했다. 예상대로 unitId와 request가 null이기 때문에 native 코드인 loadAd를 실행하지 못하고 requestAd 함수가 종료되었다. 이렇게 되는 이유가 뭘지 추정했다.
1) Banner 광고를 반환하는 곳 발견
- App의 컴포넌트 호출 부분
<BannerAd unitId={adUnitId} size={BannerAdSize.LARGE_BANNER} //Large banner: 320 x 100 density-independent pixels requestOptions={{ requestNonPersonalizedAdsOnly: false, }} onAdFailedToLoad={error => { console.log('Advert failed to load: ', error); setShowBanner(false); setAltanativeText('광고 자리입니다.'); }} />
- BannerAd.js의 BannerView 호출 부분
파일 경로: root폴더\node_modules\@react-native-firebase\admob\lib\ads\BannerAd.jsreturn ( <ReactNativeFirebaseAdMobBannerView size={size} style={style} unitId={unitId} request={validateAdRequestOptions(requestOptions)} onNativeEvent={onNativeEvent} /> );
여기서 느낌이 왔다. props 순서가 size, unitId, request 순서였다.
<공식 문서>
Properties that are to be reflected in JavaScript needs to be exposed as setter method annotated with @ReactProp (or @ReactPropGroup).JavaScript의 속성은 @ReactProp으로 annotate된 setter method로 반영되어야 한다. request가 셋 중 맨 마지막 props였으므로 java파일의 setSize에서 호출하는 requestAd 함수를 setRequest로 옮겨보았다.
@ReactProp(name = "request") public void setRequest(ReactViewGroup reactViewGroup, ReadableMap value) { Log.d("TEST_ADMOB", "setRequest"); request = ReactNativeFirebaseAdMobCommon.buildAdRequest(value); + requestAd(reactViewGroup); }
2) 디버깅 결과
2022-01-30 23:08:27.329 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: setSize 2022-01-30 23:08:27.329 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: setUnitId 2022-01-30 23:08:27.329 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: setRequest 2022-01-30 23:08:27.330 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: 320x100_as 2022-01-30 23:08:27.330 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: ca-app-pub-*****/***** 2022-01-30 23:08:27.330 31512-31512/com.howwwwwhy.heartwarming D/TEST_ADMOB: com.google.android.gms.ads.AdRequest@d26fc53
size, unitId, request 모두 값이 들어가 있었고 정상적으로 광고를 요청했다.
3) patch 파일 생성
- patch-package 사용
npx patch-package @react-native-firebase/admob
root의 patches 폴더에
@react-native-firebase+admob+11.5.0.patch 파일이 생성된다.
- 생성된 파일을 github에 업로드한다.
https://github.com/HoWWWWWhy/Heartwarming/tree/master/patches
setter method에서 함수를 호출하는 것은 좋지 않은 방법인 것 같다. props의 순서에 상관없이 requestAd 함수는 동일하게 호출되어야 한다.
추가로 시간이 날 때, Android Native UI Components와 admob 모듈에 대한 코드를 좀 더 이해하여 props 순서와 관계없이 같은 동작을 할 수 있도록 할 예정이다.
보통 npm 모듈을 수정하게 되는 경우, 다음 버전에서는 문제가 개선되리라 믿게 된다. 하지만 슬프게도 issue의 comment를 읽던 중 아래 내용을 발견했다.
https://github.com/invertase/react-native-firebase/issues/5179#issuecomment-844355532
mikehardy commented on 20 May 2021
With apologies Admob no longer present in v12 as it was removed in upstream SDKs so there are no APIs to wrap any longer - please stay with v11.5 until migration to another project (either from this same code in a new repo - planned, or a different project)
현재(2022년 1월 30일) @react-native-firebase/admob의 최신 버전이 11.5.0이고 함께 설치되어야 하는 @react-native-firebase/app의 최신 버전은 14.3.0이다. 하지만 admob을 쓰기 위해서는 app도 11.5.0으로 동일하게 맞춰주어야 한다. comment에 의하면 v12부터 admob이 제외되었고 실제로 github에서 react-native-firebase/packages 폴더를 확인해보니 정확히 v12.0.0부터 admob 폴더가 빠졌다. 이걸 왜 지금 알게 된 걸까...
만약 firebase/app의 다른 패키지를 사용해야 할 날이 온다면,
admob 모듈을 다른 모듈로 바꿔야 할 것이다. 마음의 준비를 할 수 있게 된 것으로 만족해야겠다.