Story 목록으로
블로그2026-08-17

"의도적으로 숨겨진 기능이 있습니다" — Touch Rush 심사 반려, 코드를 처음부터 다시 훑었습니다

Touch Rush 업데이트 심사에서 Guideline 5.6(숨겨진 기능 의심) 반려를 받았습니다. 코드에는 그런 로직이 없었고, 원인을 추적한 과정과 Apple에 보낸 답변을 그대로 공유합니다.

앱을 1인 개발자 체제로 운영하다 보면 크고 작은 심사 반려를 겪게 됩니다. 대부분은 사유가 명확해서 고치면 끝나는데, 이번엔 조금 달랐습니다. Touch Rush 업데이트를 App Store Connect에 올렸더니, "자동 분석 결과 심사 과정에서 의도적으로 숨겨진 것으로 보이는 기능이 있다"는 Guideline 5.6(개발자 행동 강령) 반려 통보가 왔습니다. 정확히 어떤 화면이나 기능을 지목한 것인지는 메시지에 나와 있지 않았습니다.

일단 코드부터 다시 훑었다

가장 먼저 든 생각은 "정말로 숨긴 기능이 있나?"였습니다. 심사 우회 목적으로 흔히 문제가 되는 패턴들을 하나씩 짚어봤습니다.

  • 원격 설정(Remote Config)이나 서버에서 기능을 켜고 끄는 코드 — 없음
  • 리뷰어·TestFlight·특정 기기나 지역을 감지해 다르게 동작하는 로직 — 없음
  • 로그인이나 인앱결제로 잠긴 콘텐츠 — 없음 (애초에 IAP 자체를 쓰지 않음)
  • WebView로 외부 콘텐츠를 불러와 승인 후 내용을 바꿔치기하는 구조 — 없음

Touch Rush는 오프라인 반응속도 게임(동물 터치) + AdMob 광고 + 로컬 저장(SharedPreferences)이 전부입니다. 사용자가 누구든, 기기가 무엇이든 똑같은 코드 경로로 동작합니다. 그렇다면 Apple의 자동 분석은 무엇을 보고 "숨겨졌다"고 판단했을까요?

유력한 원인 하나를 찾았다

의심이 간 곳은 게임오버 화면의 "부활하기" 버튼이었습니다. 생명을 모두 잃으면 리워드 광고를 시청하고 이어서 플레이할 수 있는 버튼인데, 코드를 보니 이 버튼이 광고가 실제로 로드됐는지와 무관하게 항상 눌리는 상태로 떠 있었습니다.

// 광고 로드 여부와 관계없이 항상 부활 버튼이 활성화되어 있음
ElevatedButton(
  onPressed: () { ...; _showReviveAd(); },
  child: const Text('부활하기'),
),

버튼을 눌렀을 때 광고가 준비돼 있으면 정상적으로 리워드 영상이 재생되지만, 준비돼 있지 않으면 "로딩 중... 잠시 후 재시도"라는 짧은 안내만 뜨고 아무 일도 일어나지 않습니다. 만약 심사 세션 당시 리워드 광고가 하필 채워지지 않은 상태였다면, 심사자(혹은 자동 분석) 입장에서는 눌러도 반응 없는 버튼 — 즉 "뭔가 감춰진 기능처럼" 보였을 가능성이 있습니다.

몇 가지 정황도 이 추정을 뒷받침했습니다. 연령 확인 전이거나 아동으로 판정된 세션은 비개인 맞춤 광고만 요청하도록 되어 있는데, 이런 광고는 일반적으로 채움률(광고가 실제로 채워지는 비율)이 더 낮습니다. 게다가 이 무렵 Google Play의 가족 정책 대응 과정에서 AdMob 콘솔의 광고 콘텐츠 등급 설정을 바꿨는데, 이 변경을 iOS 광고 단위에도 함께 적용했습니다. 설정 변경이 반영되기까지는 보통 몇 시간 정도 지연이 있을 수 있어서, 공교롭게 그 시점과 심사 시점이 겹쳤을 가능성도 있습니다.

물론 이건 "가장 유력한 추정"이지 확정은 아닙니다. Apple이 정확히 무엇을 근거로 판단했는지는 알 수 없고, 이전에 승인됐던 버전과 코드가 완전히 동일했는지도 검증할 방법이 없었습니다.

Apple에는 이렇게 답장했다

추정한 원인과 앱의 전체 기능 목록을 정리해서 Resolution Center에 아래와 같이 답장을 보냈습니다.

Touch Rush does not contain any remotely-configured, server-toggled, or reviewer-detection logic of any kind. The app is fully offline aside from ad requests: it is a single-player reaction game with local progress storage and no login, no in-app purchases, and no dynamically-loaded web content. Every user — including the App Review team — runs the exact same code path on every device and every session.

We believe the most likely explanation for the "hidden feature" flag is our "Revive" option (a standard rewarded-video continue mechanic on the Game Over screen). Its availability depends on AdMob's real-time ad fill — if no rewarded ad was available to serve during this review session, tapping "Revive" would show a loading/retry message instead of an ad, which may have appeared as a non-functional or concealed feature rather than an ad-fill issue.

We are happy to adjust this flow (e.g., only display "Revive" once a rewarded ad has successfully loaded) if that would help avoid ambiguity in future reviews. If there is a specific screen the review team believes was hidden, we would greatly appreciate the specifics so we can address it directly.

지금은 이렇게 하고 있어요

이 글을 쓰는 시점 기준으로, 재검토 요청을 보내고 Apple의 답변을 기다리고 있습니다. 코드 자체는 일단 수정하지 않고 결과를 지켜보기로 했는데, 만약 다시 반려된다면 "부활하기" 버튼을 광고가 실제로 로드 완료된 뒤에만 노출하는 방향으로 바꿀 계획입니다. 결과가 나오면 이 글에 이어서 업데이트하겠습니다.

이번 일로 남은 것

같은 기간 Google Play에서는 "가족 광고 형식별 요건" 위반으로 다른 반려를 받기도 했습니다. 원인은 명확했습니다 — 연령 구분 없이 모든 사용자에게 동일한 광고 설정을 적용하고 있었던 것이었고, 연령 확인 화면과 연령별 AdMob 설정 분리로 해결했습니다. 이번 Apple 건과 묶어보면 결국 하나의 교훈으로 모입니다. 아동이 함께 쓸 수 있는 앱에서는 광고 하나하나의 동작 방식이 심사 결과에 직접 영향을 줄 수 있다는 것입니다. 광고 네트워크의 실시간 채움 여부처럼 개발자가 완전히 통제할 수 없는 변수까지 감안해서 UI를 설계해야 한다는 걸, 이번 기회에 다시 확인했습니다.

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.