배너 광고가 비어 있던 이유: 실패 사유를 1주 기록해 보니 71%가 「광고 없음」이었다
AdMob 수익 앱 애드덱의 하단 배너가 자주 비어 있었는데, 왜 비었는지 답할 기록이 없었습니다. 광고 로드 실패를 사유와 함께 남기고 1주 동안 모은 결과, 배너 실패의 71%는 NO_FILL 이었습니다. 무엇을 어떻게 기록했는지, 기기에서 먼저 확인한 것, 재시도를 늘렸다가 줄인 이유를 정리합니다.
배너가 자주 비어 있다
애드덱은 AdMob 수익을 보는 앱이고, 화면 맨 아래에 배너 광고가 하나 있습니다. 개발자 폰에서 이 자리가 비어 있을 때가 많았습니다.
원인을 말하려고 GA4 를 열어 보니 광고 로드 실패 기록이 하나도 없었습니다. 10월 16일 앱 전체의 실패 기록을 정리할 때 광고 코드가 대상에서 빠져 있었습니다. AdMob 보고서로는 요청 중 몇 퍼센트에 광고가 붙었는지까지만 알 수 있고, 어떤 실행에서 무슨 일이 있었는지는 알 수 없습니다.
무엇을 남겼나
광고 로드가 실패하면 SDK 가 주는 오류 코드와 메시지를 코드:메시지 한 줄로 GA4 에 보냅니다(10월 19일 1.0.58).
| 기록 | 언제 |
|---|---|
ad_banner |
하단 배너 로드 실패 |
ad_native_start |
앱 시작 때 네이티브 광고 로드 실패 |
ad_native |
종료 팝업 네이티브 광고 로드 실패 |
ad_init |
광고 SDK 초기화 예외 |
ad_consent_info · ad_consent_form |
광고 동의 정보·양식 실패 |
giveup |
짧은 재시도까지 모두 실패 |
규칙은 두 가지입니다.
- 한 번 실행하는 동안 같은 값은 한 번만 보냅니다. 배너가 같은 이유로 여러 번 실패해도 기록은 하나입니다.
CANCELLED는 남기지 않습니다. 우리 재시도가 새 요청을 보내면 SDK 가 붙잡고 있던 앞 요청이 취소되고, 그 앞 요청의 콜백에CANCELLED가 옵니다. 실패가 아니라 우리가 만든 신호라서 세면 실패가 부풀려집니다.
기기에서 먼저 만들어 봤다
배포 전에 디버그 빌드로 실패를 일부러 만들어 기록이 실제로 남는지 봤습니다.
- 네트워크를 끄면
ad_consent_info에2:Error making request.가 남았습니다. 광고 로드 쪽은 실패 콜백이 바로 오지 않았고, 그래서 기록도 없었습니다. - 광고 단위 ID 를 없는 값으로 바꾸면 배너와 네이티브 모두
NETWORK_ERROR:HttpError(responseCode=400…)가 남았습니다. 잘못된 ID 인데도INVALID_REQUEST가 아니라NETWORK_ERROR로 옵니다. 코드 이름만 보고 판단하면 안 되고 메시지까지 남겨야 하는 이유입니다.
1주 결과
10월 19일부터 26일 오전까지, 1.0.58~1.0.63 사용자 110명의 기록입니다.
하단 배너 (실패 기록 41건 · 18명)
| 사유 | 건수 | 비율 |
|---|---|---|
NO_FILL (No fill.) |
29 | 71% |
NETWORK_ERROR (주소 못 찾음·연결 실패·시간 초과) |
8 | 20% |
TIMEOUT (JavaScript 엔진 로딩 대기) |
3 | 7% |
INVALID_REQUEST (Ad size will not fit on screen.) |
1 | 2% |
네이티브 광고
| 자리 | 실패 기록 | NO_FILL | 네트워크 | TIMEOUT |
|---|---|---|---|---|
| 종료 팝업 | 31건 | 68% | 23% | 10% |
| 앱 시작 | 16건 | 44% | 38% | 19% |
네트워크 칸에는 HTTP 504 응답(종료 3건 · 시작 1건)도 넣었습니다.
같은 기간(10월 19~25일) AdMob 보고서의 배너는 요청 1,015건, 일치율 57.0%, 노출률 92.1% 였습니다. GA4 기록은 실행마다 한 번이고 AdMob 은 요청마다 세서 숫자가 같을 수는 없지만, 방향은 같습니다. 배너가 비는 가장 큰 이유는 앱이 아니라 그 순간 붙일 광고가 없었던 것입니다. 광고가 붙은 요청은 92% 가 실제로 화면에 나왔습니다.
네트워크 오류 안에는 이런 모양이 섞여 있었습니다.
Unable to resolve host "googleads.g.doubleclick.net"— 광고 서버 주소를 못 찾음Failed to connect to googleads.g.doubleclick.net/0.0.0.0— 광고 서버 주소가0.0.0.0으로 바뀌어 있음. 광고 차단용 DNS 가 흔히 이렇게 돌려주지만, 그 기기의 설정을 확인한 것은 아닙니다.SocketTimeoutException: timeout— 연결은 됐지만 응답이 늦음
광고 동의 정보 실패는 10건이었고, 그중 8건은 네트워크를 끈 시험에서 본 것과 같은 2:Error making request. 였습니다.
재시도는 늘렸다가 줄였다
배너 재시도는 이번 기록 전에도 몇 번 바뀌었습니다.
| 시기 | 실패하면 |
|---|---|
| 9월 6일 첫 구현 | 재시도 없음 |
| 9월 24일 | 2초 간격 5회 |
| 10월 21일 | 3초 간격 5회, 그 뒤 30초마다 계속 |
| 10월 23일 | 3초 간격 2회, 그 뒤 멈춤 |
9월 25일 로그로 확인한 것이 기준이 됐습니다. AdMob 콘솔에서 켜 둔 자동 새로고침은 광고를 못 받은 뒤에도 계속 돌아서, 마지막 NO_FILL 뒤 약 110초에 배너가 채워졌습니다. 앱이 30초마다 다시 요청하면 SDK 가 이미 하는 일을 앱이 한 번 더 하는 셈입니다. 그래서 앱은 처음 몇 초의 빈칸만 메우고 나머지는 SDK 새로고침에 맡기기로 했습니다.
짧은 재시도가 얼마나 살리는지도 기록에 나옵니다. 같은 기간 배너가 짧은 재시도까지 모두 실패한 기록은 24건(giveup 21 · 10월 21일 판의 slow 3)입니다. 실패 기록 41건은 실행마다 사유별로 하나씩이라 배너가 실패한 실행은 41번 이하입니다. 그러니 배너가 한 번 실패한 실행의 절반 넘게는 몇 초 뒤 다시 물어도 광고를 받지 못했습니다. NO_FILL 은 몇 초 만에 잘 바뀌지 않습니다.
아직 모르는 것
NO_FILL이 어떤 사용자에게 몰리는지. 나라별로 나눠 보려 했지만 GA4 에서 나라가 비어 있는 사용자가 많아 아직 나누지 못했습니다.INVALID_REQUEST1건. 화면보다 큰 배너 크기를 요청했다는 메시지인데, 1건뿐이라 아직 원인을 보지 않았습니다.- 동의 정보의
NoSuchElementException2건. 1.0.61 에서만 나왔고 SDK 안에서 난 예외입니다.
정리
- 광고가 비어 있으면 고치기 전에 실패 사유부터 남깁니다.
NO_FILL이 대부분이면 앱 코드보다 광고 수요 쪽의 문제입니다. - 오류 코드 이름만 믿지 말고 메시지까지 남깁니다. 잘못된 광고 단위 ID 가
NETWORK_ERROR로 오기도 합니다. - 우리 재시도가 스스로 취소한 요청(
CANCELLED)은 실패로 세지 않습니다. - 같은 값은 실행마다 한 번만 남기면 기록이 많아지지 않습니다.
- 자동 새로고침을 켜 두었다면 앱의 재시도는 짧게 끝내고 나머지는 SDK 에 맡깁니다.
- GA4 기록 수와 AdMob 요청 수는 세는 단위가 다릅니다. 둘을 같이 볼 때는 무엇을 셌는지 적어 둡니다.
이 글에 나온 앱은 AdMob 수익 앱 애드덱(Google Play) 입니다.