app-ads.txt 는 한 줄짜리 파일이다. 어려운 건 그 한 줄을 어디에 두느냐였다.

1편과 2편에서 광고를 코드에 넣었다. 폰 한 대에서는 결과를 보고 넘어갈 때만, 같이 하기에서는 모든 폰에 동시에. 그런데 광고 SDK 가 들어간 빌드는 코드 밖의 말도 바꾼다. 스토어 설문의 답, 개인정보 처리방침, 스토어 설명 한 줄, 출시 노트. 이번 편은 그 말들을 앱과 맞춘 이야기다.

한 줄짜리 파일의 자리

AdMob 계정부터 만들었다. AdMob 과 애드센스는 한 사람당 계정 하나라서, 이미 애드센스가 붙어 있는 Google 계정으로 가입했다. 이 계정은 Play 개발자 계정과 다르다. 처음에는 그게 걸렸는데, AdMob 은 공개된 Play 앱을 패키지 이름(kr.co.addsoft.whopays)으로 찾아 연결하기 때문에 계정이 달라도 상관없었다. 결제 정보도 애드센스 쪽 것을 이어 쓰기로 했다.

앱과 전면 광고 단위를 만들고 나면 AdMob 이 app-ads.txt 에 넣을 한 줄을 준다. 형식은 이렇다.

google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0

광고 거래소가 "이 앱의 광고 자리를 정말 이 게시자가 파는가"를 확인하는 파일이다. 마지막 값은 Google 의 공개 인증 ID 라 누구나 같다. 문제는 위치다. AdMob 은 이 파일을 아무 데서나 찾지 않는다. Play 스토어 등록정보에 적힌 개발자 웹사이트의 루트에서 찾는다.

이 앱에는 마침 웹 사이트가 있었다. 개발기에서 IIS 에 올린 WebGL 판이 whopays.addsoft.co.kr 에서 돌고, 개인정보 처리방침도 같은 사이트에서 나간다. 그래서 app-ads.txt 도 WebGL 빌드 템플릿 폴더에 넣었다. 이 폴더의 파일은 빌드할 때 결과물에 그대로 복사되니, WebGL 판을 다시 올려도 파일이 사라지지 않는다. 그리고 Play Console 의 스토어 등록정보 연락처에서 웹사이트를 이 주소로 바꿨다. 스토어 페이지에 반영된 것까지 본 뒤 AdMob 에 앱 확인을 요청했다.

게시는 정식 출시기에서 처리방침용으로 만든 Web Deploy 스크립트를 넓혀서 했다. 원래 -PrivacyOnly 는 처리방침 한 파일만 올리는 옵션이었다. 이걸 -TemplateOnly 로 바꾸고, 템플릿 폴더의 정적 파일 목록(처리방침과 app-ads.txt)을 하나씩 운영본과 대조하게 했다. 옛 이름도 그대로 받는다.

foreach ($f in $templateFiles) {
    $local = [IO.File]::ReadAllText((Join-Path $templateDir $f), [Text.Encoding]::UTF8)
    $prod  = Get-ProdText $f
    $onServer[$f] = [bool]$prod
    if ($prod -and (Normalize $prod) -eq (Normalize $local)) { Write-Host "  $f : 운영과 같음" }
    else { $changed += $f }   # 운영과 다르거나 운영에 없음 → 올린다
}

운영과 다른 파일만 올리고, 백업은 운영에 원래 있던 파일만 받는다. app-ads.txt 는 처음 올리는 거라 받을 것이 없다. 게시가 끝나면 운영 주소에서 처리방침과 app-ads.txt 를 다시 받아 저장소본과 대조한다. 전체 빌드를 올릴 때는 빌드 폴더에 app-ads.txt 가 없으면 경고를 띄운다. 이 파일이 생기기 전에 만든 빌드라는 뜻이다.

AdMob 의 확인 결과는 바로 나오지 않는다. 요청 직후 상태는 "확인할 수 없음"이었다. 크롤러가 다녀가기까지 몇 분에서 하루 넘게 걸린다고 안내한다. 확인이 안 돼도 앱 출시는 막히지 않지만 광고가 거의 나오지 않는다. 그래서 1.9 출시 순서의 첫 줄을 "AdMob 앱 확인 상태 보기"로 적었다. 확인 결과는 1.9 를 올리기 전에 다시 본다.

설문은 지금 빌드를 묻는다

Play Console 의 앱 콘텐츠에는 광고, 광고 ID, 데이터 보안 같은 정책 설문이 있다. 정식 출시기에서 광고 SDK 가 없는데 「광고 있음」이 붙어 있던 걸 「없음」으로 바로잡은 그 칸들이다. 그때 세운 원칙이 이번에도 기준이 됐다. 이 설문은 앞으로의 계획을 묻지 않는다. 지금 올라가 있는 빌드를 묻는다.

광고는 1.9 에 들어간다. 프로덕션에는 광고 SDK 가 없는 1.8 이 있다. 그러니 지금 답을 바꾸면 1.8 기준으로 틀린 답이 된다. 답은 1.9 기준으로 문서에 미리 써 두고, 콘솔에는 1.9 를 올리는 때에 옮기기로 했다.

옮기는 순서에는 함정이 하나 있다. 광고 SDK 는 매니페스트에 광고 ID 권한(AD_ID)을 넣는다. 광고 ID 선언이 "아니요"인 채로 이 권한이 든 AAB 를 올리면 Play 가 그 버전의 출시를 막는다. 반대로 개발기 시절에는 "예"로 두고 권한 없는 빌드를 올리면 막힌다는 안내를 보고 "아니요"로 뒀었다. 결국 선언과 빌드는 같이 움직여야 하고, 순서는 선언이 먼저다. 그래서 1.9 출시 순서를 이렇게 적었다.

  1. AdMob 앱 확인 상태를 본다
  2. 운영 처리방침을 새 판으로 바꾼다
  3. 앱 콘텐츠의 광고·광고 ID·데이터 보안 답을 바꾸고 콘텐츠 등급 설문을 다시 낸다
  4. 실제 광고 단위로 AAB 를 빌드해 내부 테스트 트랙에 먼저 올리고, R8 을 켠 출시 빌드에서 광고가 뜨는지 본다
  5. 프로덕션에 새 버전을 만든다
  6. 스토어 설명의 한 줄을 바꾼다

4번에서 실제 단위의 광고를 볼 테스트 폰은 먼저 AdMob 테스트 기기로 등록하고, 떠도 누르지 않는다. 1편에서 테스트 단위를 빌드가 정하게 한 이유가 여기서 다시 나온다.

답의 내용은 이렇게 정리했다. 광고는 "예". 광고 ID 도 "예"이고 목적은 광고 또는 마케팅, 애널리틱스, 사기 예방·보안·규정 준수 세 가지다. 데이터 보안은 직접 짐작하지 않고 Google 이 공개한 Mobile Ads SDK 데이터 공개 문서를 그대로 옮겼다. SDK 가 모으는 것이 네 가지다.

Play 데이터 유형 SDK 가 모으는 것
위치 → 대략적인 위치 IP 주소로 추정한 대략적 위치
앱 활동 → 앱 상호작용 앱 실행, 탭, 동영상 재생
앱 정보 및 성능 → 진단 앱 실행 시간 같은 성능 정보
기기 또는 기타 ID 광고 ID, 앱 세트 ID

넷 다 수집 예, 공유 예, 필수다. 전송 중 암호화는 예, 삭제 요청은 앱과 개발자가 보관하는 게 없어 "아니요"로 답한다. 광고 ID 는 기기 설정에서 재설정하거나 삭제한다. 음성 입력, 정산, 계좌는 정식 출시기에서 검토한 그대로 수집이 아니다. 광고 때문에 "예"가 됐으니 광고 항목만 더했다.

대상 연령은 그대로 만 13세 이상이다. 13세 미만이 대상에 없으니 가족 정책의 광고 SDK 인증 요건은 해당하지 않는다.

매니페스트가 말해 주는 것

처리방침에는 앱이 쓰는 권한 목록이 있다. 예전 판은 광고를 넣을 걸 전제로 AD_ID 하나를 미리 적어 두었다. 그런데 실제로 SDK 를 넣고 1.9 APK 를 aapt 로 열어 보니 광고 쪽 권한은 하나가 아니었다. 기존 여섯 개에 AD_ID, Android 광고 개인정보 보호 기능용 ACCESS_ADSERVICES_AD_ID·ACCESS_ADSERVICES_ATTRIBUTION·ACCESS_ADSERVICES_TOPICS, 그리고 WAKE_LOCK·FOREGROUND_SERVICE 가 더해져 있었다. 모두 SDK 가 의존성으로 함께 넣는 권한이다.

미리 써 둔 문서가 실제 빌드보다 좁았던 거다. 짐작으로 쓴 목록은 빌드가 바뀌면 틀린다. 그래서 목록은 빌드한 파일에서 읽어 옮겼고, 광고 관련 권한은 광고 SDK 가 넣는 것이라는 문장을 붙였다. 이 권한들은 Play 의 민감 권한 선언 양식 대상이 아니다. 유형이 붙은 포그라운드 서비스 권한도 없다. 콘솔이 혹시 포그라운드 서비스 선언을 요구하면 그때 다시 보기로 메모만 남겼다.

광고 항목도 다시 썼다. 전에는 "광고를 표시할 수 있습니다" 정도였다. 이제는 광고가 언제 나오는지가 정해졌으니 그대로 적었다. 게임 결과를 보고 다음 판이나 다른 게임으로 넘어갈 때 가끔, 세 판에 한 번 이하로 화면 전체 광고가 나오고, 게임 중에는 나오지 않는다. 수집 항목에는 앱 세트 ID 와 이용 정보를 더했고, 개발자는 이 정보를 받거나 보관하지 않는다는 문장도 넣었다. 1편에서 정한 광고 등급 T 도 적었다. 아동·청소년 항목의 "아동·청소년을 대상으로 한 광고를 하지 않습니다"는 "광고는 청소년 이용가(T) 수준까지만 나오도록 제한합니다"로 바꿨다. 광고는 나오니 앞 문장은 이제 정확하지 않다. Android 13 이상에서 광고 주제와 광고 측정을 끄는 방법도 이용자의 권리 항목에 더했다.

새 판은 저장소에만 있다. 운영 사이트에는 1.9 를 올리는 날, 출시 순서 2번에서 시행일을 그날로 고쳐 올린다. 지금 올리면 광고가 없는 1.8 에 대해 "판 사이에 광고가 나온다"고 말하는 처리방침이 된다. 정식 출시기에서는 처리방침을 앱보다 먼저 올렸는데, 이번에는 너무 먼저 올리지 않는 것도 같은 문제였다.

스토어 한 줄과 출시 노트

스토어 자세한 설명의 "이런 점이 좋아요"에는 "앱이 수집하는 개인정보 없음"이라는 줄이 있었다. 광고 ID 를 쓰는 순간 이 줄은 틀린 말이 된다. 데이터 보안 답과 나란히 놓이면 정책 위반으로 보일 수도 있다. 그래서 두 줄로 나눴다.

· 이름·기록·계좌 정보는 내 폰에만 저장돼요
· 광고는 판 사이에 가끔만, 게임 중에는 나오지 않아요

앞 줄은 여전히 사실인 부분만 남겼고, 뒤 줄은 광고가 어떻게 나오는지를 앞에서 말한다. "광고 없음"은 원래 쓰지 않았으니 지울 문구는 없었다.

출시 노트에도 광고 줄을 숨기지 않고 넣기로 했다. 업데이트를 받은 사람이 세 판째에 갑자기 광고를 보고 놀라는 것보다, 노트에서 먼저 읽는 편이 낫다. 대신 "무료", "광고 적음" 같은 홍보 표현은 쓰지 않는다. 사실만 적는다.

<ko-KR>
• 판을 마치고 넘어갈 때 가끔(세 판에 한 번 이하) 광고가 나와요. 게임 중에는 나오지 않아요
• 같이 하기 중 앱이 잠깐 뒤로 가도 연결이 끊기지 않게 고쳤어요

같이 하기는 모든 폰이 최신 버전이어야 함께할 수 있어요.
</ko-KR>

두 번째 줄은 2편에서 연결 확인을 소켓 스레드로 옮긴 결과다. 광고 때문에 고쳤지만, 전화를 받거나 홈 버튼을 눌러도 같이 하기가 끊기지 않는 건 모든 사람이 느낄 개선이다. 마지막 줄은 규약이 13 으로 바뀌어서 넣었다. 1.8 폰은 1.9 방에 들어오지 못한다. 출시 뒤에 "앱 버전이 달라요" 문의가 올 수 있다는 것도 출시 순서 끝에 적어 두었다. 이 노트는 아직 초안이다. 1.9 의 다른 변경이 끝나면 위쪽에 목록을 더하고 500자 안에서 다듬는다.

1.9 는 아직 스토어에 나가지 않았다. 출시 순서와 답, 처리방침 새 판, 스토어 문구, 출시 노트 초안이 저장소에 준비돼 있을 뿐이다. 나간 뒤에는 Android vitals 와 리뷰에서 광고에 대한 반응을 보고, AdMob 보고서에서 노출과 No fill 비율을 본다. 핫스팟으로 하는 같이 하기가 많다면 No fill 이 꽤 높게 나올 것이다.

광고를 넣는 일은 코드보다 말을 고치는 일이 더 길었다. 코드는 빌드를 바꾸고, 빌드는 매니페스트를 바꾸고, 매니페스트는 설문과 처리방침과 스토어 설명을 바꾼다. 그 사슬의 어느 고리도 짐작으로 쓰지 않고, 빌드한 파일에서 읽어 오는 것. 이번 연재에서 가장 오래 남을 규칙은 그거다.