iOSAndroidApp StoreGoogle PlayRelease

App Store / Google Play 리젝 사례집과 심사를 통과하기 위한 실전 체크리스트

Sloth255
Sloth255
·3 min read·559 words

앱을 출시하려고 했는데 심사에서 반려된다. 많은 개발자가 한 번쯤은 겪는 장벽입니다.

게다가 심사에서 멈추는 이유는 크래시나 빌드 문제만이 아닙니다. 권한 설명, privacy policy, 결제 동선, 스토어 메타데이터, 심사 코멘트 부족처럼 구현 이외의 운영 문제 때문에 막히는 일도 드물지 않습니다.

이 글에서는 App Store와 Google Play 심사에서 자주 발생하는 리젝 사례를 카테고리별로 정리하고, 제출 전에 확인해야 할 포인트를 실무 관점에서 정리합니다.


대상 독자

  • 처음으로 App Store / Google Play에 앱을 제출하는 분
  • 과거에 리젝을 받은 적이 있고 원인을 정리하고 싶은 분
  • React Native / Flutter 같은 크로스플랫폼으로 개발하는 분
  • 출시 전 체크리스트를 정비하고 싶은 팀

먼저 알아둘 용어

이 글에서 나중에 나오는 용어를 먼저 짧게 정리해 둡니다. 여기만 잡고 가도 뒤쪽 리젝 사례를 읽기가 훨씬 쉬워집니다.

  • reviewer: Apple 또는 Google의 심사 담당자. 앱 본체뿐 아니라 스토어 설명문, 스크린샷, 신고 정보, 심사 코멘트까지 확인합니다.
  • IAP (In-App Purchase): Apple의 앱 내 결제 방식. 디지털 콘텐츠나 앱 내 기능 판매에 사용됩니다.
  • ATT (App Tracking Transparency): 다른 회사의 앱이나 웹사이트를 가로지르는 추적 전에 사용자에게 허가를 묻는 iOS 메커니즘입니다.
  • IDFA: 광고 측정에 쓰이는 식별자입니다. 이 구성을 사용하면 ATT와 App Privacy를 함께 확인해야 하는 경우가 많습니다.
  • Data safety / App Privacy: Google Play / App Store에서 제출하는 데이터 수집 자기 신고입니다. SDK를 추가했는데 신고를 갱신하지 않으면 정합성이 깨집니다.
  • target SDK: Android 앱이 어떤 API level을 전제로 동작하는지를 나타내는 값입니다. minSdkVersion 은 최저 지원 OS를 뜻하므로 의미가 다릅니다.
  • 스토어 메타데이터: 스크린샷, 설명문, 부제목, 카테고리, 연령 등급, 신고 항목처럼 스토어에 표시하거나 제출하는 정보 전체를 뜻합니다.
  • WebView: 앱 안에서 웹페이지를 표시하는 컴포넌트입니다. 편리하지만 화면 대부분을 WebView에 의존하면 네이티브 가치가 약해 보이기 쉽습니다.
  • UGC (User Generated Content): 사용자 생성 콘텐츠입니다. 댓글, 이미지 업로드, 프로필, 리뷰 등이 포함됩니다.
  • restore purchases: 예전에 구매한 상태를 복원하는 동선입니다. 구독이나 일회성 구매 복원 확인에서 중요합니다.
  • entitlement: Apple Capability 또는 특정 기능을 사용하기 위한 권한 설정입니다. 대부분은 일반 앱에서도 쓸 수 있지만, 일부 특수 entitlement는 Apple 승인이나 추가 조건이 필요합니다.

먼저 이해해야 할 심사 측의 시각

리젝을 줄이려면 가이드라인만 읽어서는 부족합니다. 심사 담당자가 짧은 시간 안에 무엇을 확인하는지 이해하는 편이 우선순위를 정하기 쉽습니다.

특히 자주 멈추는 상태는 다음과 같습니다.

  • 구현은 되어 있지만 설명이 부족하다
  • 권한을 요청하지만 기능과의 관계가 보이지 않는다
  • 결제가 있는데 가격이나 해지 동선이 모호하다
  • 스크린샷이나 설명문과 실제 앱이 일치하지 않는다
  • reviewer 가 로그인하지 못해 핵심 기능을 확인할 수 없다

심사에서는 UI의 화려함보다 먼저 안전성, 정합성, 완성도를 봅니다.

관점 확인하는 것 흔한 반려 예시
완성도 크래시가 없는지, 미구현 요소가 없는지 실행 직후 크래시, 플레이스홀더 방치
개인정보 데이터 수집과 설명이 일치하는지 권한 설명 부족, Privacy URL 없음
결제 결제 규칙을 지키는지 외부 결제 유도, 구독 설명 부족
메타데이터 스토어 정보와 구현이 일치하는지 과장된 스크린샷, 잘못된 신고
심사 동선 reviewer 가 검증하기 쉬운지 로그인 불가, 검증 절차 부족

App Store와 Google Play의 차이를 먼저 정리하기

둘 다 "안전하고, 오해를 부르지 않으며, 미완성이 아닌 것"을 보지만, 자주 막히는 쟁점에는 약간 차이가 있습니다.

관점 App Store에서 더 강하게 보는 포인트 Google Play에서 더 강하게 보는 포인트
완성도 UI의 자연스러움, 미완성 느낌, 크래시 구현 결함에 더해 신고 내용과의 차이
개인정보 권한 설명문, ATT, App Privacy permissions, Data safety, 광고 신고
결제 IAP 규칙, Reader App 조건, 구독 표기 과장 표현, 불명확한 결제 설명, 정기 구독의 명확성
기술 요구 심사 중 동작, 실기기 안정성 target SDK, 위험 권한, SDK 정책 적합성
운영 측면 reviewer 가 검증 가능한지 Console 측 선언, closed testing, production access gate

대략적으로 말하면 App Store는 앱 경험과 결제 / 개인정보 설명의 엄밀성에서, Google Play는 기술 요구와 자기 신고의 정합성에서 더 자주 막힙니다.

그래서 같은 앱이라도 준비 순서는 조금 달라집니다.

  • App Store: 실기기 동작, 결제 화면, 권한 문구, 심사 코멘트를 먼저 다듬기
  • Google Play: target SDK, permissions, Data safety, 광고 신고를 먼저 다듬기

실제 반려 문구와 공개 차단 문구로 보는 케이스 스터디

여기서는 실제로 자주 보이는 App Store 심사 문구와 Google Play 공개 차단 문구를 인용하면서, "그 한 문장이 무엇을 의미하는지"와 "무엇을 먼저 고쳐야 하는지"를 정리합니다. 뒤쪽 카테고리는 이 케이스의 상세 해설처럼 읽으면 활용하기 쉽습니다.

Case 1: App Store에서 실행 직후 크래시

"iOS 17.0이 설치된 iPhone을 Wi-Fi 환경에서 사용해 앱을 심사하는 동안 하나 이상의 bug를 발견했습니다. 구체적으로 앱이 실행 직후 바로 크래시했습니다."

이것은 일시적 실패가 아니라 심사 기기에서 재현되는 중대한 결함으로 취급됩니다. 먼저 해야 할 일은 비행기 모드, 최초 설치, 빈 계정, 느린 네트워크에서 재현을 확인하고 실행 직후 수행되는 처리들을 전부 나열하는 것입니다.

Case 2: App Store에서 Privacy Policy 부족

"앱이 사용자 또는 사용 데이터를 수집하지만 privacy policy URL이 없습니다."

이 말은 SDK나 자체 API를 통해 어떤 데이터든 다루고 있는데, 사용자가 볼 수 있는 공개 설명이 부족하다는 뜻입니다. 먼저 해야 할 일은 공개 URL 하나를 준비하고, 수집 데이터, 이용 목적, 제3자 제공 여부, 문의처를 명시한 뒤 App Store Connect 설정과 App Privacy를 맞추는 것입니다.

Case 3: App Store에서 디지털 결제가 IAP가 아님

"앱에 디지털 콘텐츠를 구매할 수 있는 기능이 있지만, 구매 메커니즘이 in-app purchase를 사용하지 않습니다."

이것은 가격 표시 방식의 문제가 아니라, 판매 대상의 성격에 비해 결제 방식이 잘못되었다는 지적입니다. 먼저 해야 할 일은 무엇을 팔고 있는지 기능 단위로 분해하고, 앱 안에서 소비되는 디지털 가치라면 IAP 전제로 다시 맞추는 것입니다.

Case 4: Google Play에서 target API 요건 미충족

"새 앱을 게시하려면 Android 15 (API level 35) 이상을 타깃으로 해야 합니다."

Google Play는 전통적인 rejection mail뿐 아니라 제출 전에 요구사항 차단 형태로 조건을 표시하기도 합니다. 이 문구가 보이면 앱 품질 이전에 공개 요건을 충족하지 못한 상태입니다. 먼저 해야 할 일은 targetSdkVersion 뿐 아니라 의존 라이브러리, 권한 차이, 알림과 백그라운드 동작까지 포함해 업데이트 계획을 세우는 것입니다.

Case 5: Google Play의 신규 personal account가 production으로 못 넘어감

"앱에 대해 최소 12명의 tester가 최근 14일 연속으로 opted-in 상태를 유지한 closed test를 실행해야 합니다."

첫 공개에서는 앱의 완성도와 별개로 account 유형 때문에 production gate에서 막히는 경우가 있습니다. 새로운 personal account는 closed test와 production access 조건을 채우지 않으면 공개 자체로 진행할 수 없습니다. 여기에 더해 account owner의 Android 실기기 device verification이 필요한 경우도 있습니다.

먼저 해야 할 일은 대상 account가 새로운 personal account인지 확인하고, 12명 / 14일 closed test, tester의 지속 opt-in, Play Console mobile app을 통한 device verification을 가능한 빨리 끝내는 것입니다.

App Store는 심사 코멘트로 반려되는 경우가 많고, Google Play는 Console 상의 요구사항 차단이나 신고 차이로 멈추는 경우가 많습니다. 하지만 본질은 같고, 대부분은 "미완성", "설명 부족", "신고 불일치", "공개 요건 미달" 중 하나로 분류할 수 있습니다.


카테고리 1: 앱 완성도

1. 실행 직후 크래시한다

첫 실행 시 API 오류, 로컬 DB 초기화 실패, nil 참조 등은 가장 전형적인 반려 사유 중 하나입니다. 여기서 말하는 nil 참조는 아직 값이 들어오지 않은 객체를 읽으러 가서 앱이 죽는 패턴입니다. 특히 reviewer 의 기기 환경은 내 손에 있는 환경과 완전히 같지 않으므로, 실행 직후의 안전성은 과할 정도로 지키는 편이 낫습니다.

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                    setupApp()
                }
        }
    }

    private func setupApp() {
        do {
            try initializeDatabase()
        } catch {
            print("DB init failed: \(error)")
        }
    }
}

확인하고 싶은 점:

  • 첫 실행 시 계정이 없어도 크래시하지 않는다
  • 네트워크가 끊겨도 앱 자체는 실행된다
  • 필수 데이터 취득에 실패해도 fallback 화면으로 이동할 수 있다

효과적인 해결책은 실행 처리를 다음 두 종류로 나누는 것입니다.

  • 반드시 성공해야만 화면을 보여줄 수 있는 처리
  • 실패해도 나중에 재시도할 수 있는 처리

예를 들어 remote config를 가져오거나 추천 목록을 미리 읽는 것 때문에 앱 전체를 멈출 필요는 없습니다. 먼저 홈 화면이나 최소한의 골격을 보여주고, 실패한 처리만 다시 시도하도록 만들면 심사 환경 차이에서 훨씬 덜 무너집니다.

실무에서는 다음 순서로 고치면 빠른 편입니다.

  • crash log를 수집한다
  • 실행 시 수행하는 초기화 처리를 전부 나열한다
  • 동기적으로 반드시 성공해야 하는 처리를 줄인다
  • 비행기 모드, 최초 설치, 빈 데이터 상태에서 실기기 확인을 한다

2. 기능이 미구현이거나 플레이스홀더가 남아 있다

"Coming Soon"만 보이는 화면, 눌러도 아무 반응이 없는 버튼, 끊어진 링크는 미완성 앱으로 판단되기 쉬운 지점입니다.

제출 직전에는 기능 추가보다 다음 항목을 먼저 정리하는 편이 안전합니다.

  • 동선만 있고 아직 구현되지 않은 기능은 제거한다
  • 더미 텍스트와 테스트 문구를 지운다
  • 에러 화면과 빈 상태 표시를 최소한의 수준까지 정리한다

3. 로그인하지 않으면 아무것도 확인할 수 없다

로그인이 필수인 앱이라도 reviewer 가 핵심 기능에 도달하지 못하면 반려되기 쉽습니다.

필요한 것은 단순한 계정 정보가 아니라 가장 짧게 검증할 수 있는 동선입니다.

  • 데모 계정을 준비한다
  • 2FA가 필요하면 절차를 적는다
  • 특정 역할에서만 보이는 기능이 있다면 보충 설명을 넣는다
  • sandbox data가 필요하면 초기 상태를 설명한다

대응의 기본은 "심사 담당자가 최단 거리로 가치를 확인할 수 있는 상태"를 만드는 것입니다. 이상적인 형태는 회원가입이나 문의 없이 주요 화면을 볼 수 있는 read-only 심사용 계정을 준비하는 것입니다.

여기서 말하는 sandbox data는 실제 사용자에게 영향을 주지 않는 심사 / 검증용 더미 데이터입니다. 주문 이력, 게시물, 구독 상태, 지도 위의 매장 데이터처럼 비어 있으면 가치가 잘 전달되지 않는 정보는 미리 넣어 두는 편이 검토하기 쉽습니다.

SMS 인증이나 초대제 구조가 있다면, 그것을 그대로 reviewer 에게 떠넘기지 않는 편이 안전합니다.

  • 유효기간이 긴 심사용 계정을 발급한다
  • 2FA가 필요하면 백업 코드나 대체 절차를 제공한다
  • 초기 데이터가 비어 있으면 가치가 전달되지 않는 경우 테스트 데이터를 미리 넣어 둔다
  • 뒤에서 설명하는 "신청 코멘트에 적어야 할 것" 템플릿으로 도달 절차까지 쓴다

카테고리 2: 개인정보와 권한

4. privacy policy URL이 없다

사용자 데이터나 사용 데이터를 다루는데 Privacy Policy URL이 없는 상태는 매우 위험합니다. 이것은 앱 본체의 문제라기보다 제출 정보의 미비로 멈추는 케이스에 가깝습니다.

최소한 privacy policy에는 다음 내용을 명시해 두는 것이 좋습니다.

  • 수집하는 데이터 종류
  • 이용 목적
  • 제3자 제공 여부
  • 보관 기간
  • 사용자 삭제 / 정정 요청 방법

여기서 헷갈리기 쉬운 점은 Privacy PolicyApp Privacy / Data safety 는 별개라는 것입니다.

  • Privacy Policy: 사용자가 읽을 수 있는 공개 문서
  • App Privacy / Data safety: 스토어에 제출하는 자기 신고

둘 중 하나만으로는 부족하고, 내용도 서로 일치해야 합니다.

해결책으로는 먼저 공개 페이지 한 장을 준비해 무엇을 수집하는지, 무엇에 사용하는지, 문의처가 무엇인지 적는 것이 가장 빠릅니다. 그 뒤 사용 중인 SDK 목록과 대조하면서 App Store Connect / Play Console 신고를 갱신합니다.

5. 쓰지 않는 권한을 요청한다

위치, 연락처, 마이크, 카메라 같은 권한은 기능과 강하게 연결되어 있지 않으면 불신을 부릅니다. 라이브러리를 넣은 결과 불필요한 권한이 섞여 들어가는 경우도 많습니다.

import { Alert } from 'react-native'
import * as Location from 'expo-location'

async function requestLocationForMap() {
  const { status } = await Location.requestForegroundPermissionsAsync()
  if (status !== 'granted') {
    Alert.alert('지도에서 주변 매장을 표시하려면 위치 권한이 필요합니다')
    return
  }

  // 지도 표시 흐름
}

핵심은 쓰는 권한만, 쓰는 순간에 요청하는 것입니다.

실무 팁은 권한 요청 코드를 찾는 것에 그치지 말고, 의존 라이브러리가 추가한 선언도 함께 보는 것입니다. Android에서는 AndroidManifest.xml, iOS에서는 Info.plist 와 도입한 SDK를 함께 보지 않으면 의도하지 않은 권한이 남기 쉽습니다.

6. 권한 설명문이 지나치게 추상적이다

"더 나은 경험을 위해 사용합니다" 같은 설명으로는 무엇을 위한 권한인지 전달되지 않습니다. 설명문은 구체적인 기능과 연결되어 있어야 합니다.

좋은 예:

  • 지도 위에 주변 매장을 표시하기 위해
  • 프로필 사진을 촬영하기 위해
  • 바코드를 읽기 위해 카메라를 사용하기 위해

약한 예:

  • 경험 향상을 위해
  • 편의성 향상을 위해
  • 서비스 품질 개선을 위해
<key>NSLocationWhenInUseUsageDescription</key>
<string>지도에서 주변 매장을 표시하기 위해 사용합니다</string>

<key>NSCameraUsageDescription</key>
<string>프로필 사진을 촬영하기 위해 사용합니다</string>

7. App Tracking Transparency에 대응하지 않았다

IDFA를 사용하는 경우 ATT 다이얼로그와 App Privacy 신고의 정합성이 필요합니다. 또한 추적 허가를 받지 못해도 앱이 동작하도록 설계해 두어야 합니다.

여기서 ATT 는 광고 측정이나 attribution에 쓰이는 IDFA 에 접근하기 전에 사용자에게 허가를 구하는 장치입니다. 실무적으로는 이렇게 이해하면 충분합니다. 광고 SDK를 넣었거나, 측정 SDK가 IDFA를 읽거나, 제3자 데이터와 결합해 사용자를 추적한다면 심사 대상이 되기 쉽습니다.

import AppTrackingTransparency

func requestTrackingPermission() {
    ATTrackingManager.requestTrackingAuthorization { status in
        switch status {
        case .authorized:
            break
        case .denied, .restricted, .notDetermined:
            break
        @unknown default:
            break
        }
    }
}

수정 순서는 다음이 현실적입니다.

  • 어떤 SDK가 IDFA나 tracking을 사용하는지 목록화한다
  • tracking이 정말 필요한지 다시 본다
  • 불필요하면 SDK 설정이나 의존성을 제거해 ATT 자체를 없앤다
  • 필요하면 ATT 다이얼로그, App Privacy, 앱 내 설명을 맞춘다
  • 허가를 받지 않아도 주요 기능이 동작하는지 확인한다

카테고리 3: 결제와 비즈니스 모델

8. 디지털 콘텐츠를 외부 결제로 유도한다

앱 내 디지털 상품이나 기능 결제를 다루는 경우 App Store에서는 IAP 사용이 기본입니다. 웹 결제나 외부 사이트 구매로 유도하면 높은 확률로 막힙니다.

정리하면 다음 이해가 중요합니다.

  • 실물 상품이나 대면 서비스는 외부 결제도 가능하다
  • 디지털 콘텐츠나 프리미엄 기능은 원칙적으로 IAP다
  • Reader App에서 외부 사이트의 계정 생성 / 관리 링크를 보여주려면 External Link Account Entitlement가 필요하다
  • Reader App 예외가 있다고 해도 대상 카테고리와 구현 조건은 상당히 제한적이다

IAP 를 써야 할지 고민될 때는 "앱 안에서 소비되는 디지털 가치인가"를 기준으로 보면 정리하기 쉽습니다.

  • IAP 쪽에 가까운 것: 광고 제거, 프리미엄 기능, 게임 화폐, 기사 무제한 열람, 동영상 무제한 시청
  • 외부 결제가 허용되는 경우가 많은 것: 실물 상품, 호출 서비스, 대면 레슨, 음식 배달, 숙박 예약

이미 외부 결제로 유도하고 있다가 막혔다면, 보통은 다음 셋 중 하나로 조정해야 합니다.

  • IAP 전제의 결제 설계로 다시 구성한다
  • 정말 Reader App 조건에 해당하는지 정밀하게 확인한다
  • 디지털 상품과 실물 서비스를 분리하고 앱 내 동선을 정리한다

9. 구독 설명이 약하다

구독에서는 가격, 갱신 주기, 자동 갱신, 해지 동선이 모호하면 사용자 보호 관점에서 약하게 보입니다.

결제 화면이나 스토어 설명에는 최소한 다음이 필요합니다.

  • 플랜명, 기간, 제공 내용이 보인다
  • 총 청구 금액이 명시된다. 연간 요금제라면 연간 총액이 가장 명확하게 보여야 한다
  • 갱신 주기와 자동 갱신 여부가 보인다
  • 무료 체험 후 정상 과금액이 보인다
  • Terms of Service 와 Privacy Policy 링크가 있다
  • 기존 구독자를 위한 sign in 또는 restore purchases 동선이 있다
  • 해지 방법이 보인다

흔한 실패는 "월 환산 금액만 크게 보여주고 실제 청구 금액과 갱신 조건을 이해하기 어렵게 만드는" 패턴입니다. 연간 플랜이라면 연간 총액을 한눈에 보이게 하고, 그 아래에 보조적으로 월 환산 금액을 적는 편이 더 안전합니다.

구독 화면을 점검할 때는 다음 요소들로 정리하면 쉽습니다.

  • 플랜명
  • 청구 시점
  • 총 청구 금액
  • 무료 체험 후 정상 가격
  • 복원 동선
  • 이용약관과 privacy policy
  • 관리 화면 또는 해지 동선

예를 들어 "7일 무료, 그 이후 연 7,200엔으로 자동 갱신"처럼 무료 기간 이후에 무엇이 일어나는지까지 한 문장으로 읽히게 해 두면 심사에서도 오해를 줄이기 쉽습니다.

또한 심사에서 막히기 어려운 구매 화면으로 만들려면 사용자가 헷갈리기 쉬운 논점을 화면 안에서 바로 해소하는 것이 효과적입니다.

  • 어떤 플랜이 선택되어 있는가
  • 지금 얼마가 청구되는가
  • 무료 기간 종료 후 얼마가 되는가
  • 언제까지 해지해야 다음 과금을 막을 수 있는가
  • 기존 구독자는 어디서 복원할 수 있는가
// React Native만으로는 StoreKit의 system-provided UI를 직접 열 수 없으므로,
// iOS 네이티브 구현을 브리지할 수 없을 때의 fallback 예시입니다.
import { Linking, Text, TouchableOpacity } from 'react-native'
다음과 같이 수정했습니다:
function SubscriptionSettings() {
  return (
    <TouchableOpacity
      onPress={() => Linking.openURL('https://apps.apple.com/account/subscriptions')}
    >
      <Text>구독 관리</Text>
    </TouchableOpacity>
  )
}

iOS 네이티브 구현이 가능하다면 Apple이 안내하는 showManageSubscriptions(in:) 를 사용해 system-provided management UI를 여는 편이 더 자연스럽습니다.

10. Reader App 예외를 너무 넓게 해석한다

Reader App 예외는 편리해 보이지만 대상이 상당히 제한되어 있습니다. 주 기능이 잡지, 신문, 서적, 오디오, 음악, 비디오 중 하나여야 하며, 외부에서 이미 구매한 콘텐츠나 구독을 앱 안에서 열람 / 재생하는 형태가 전제입니다.

또한 외부 사이트의 계정 생성 / 관리 링크를 노출하려면 External Link Account Entitlement가 필요합니다. iOS / iPadOS / tvOS에서 IAP를 제공하는 앱은 이 entitlement 대상이 아니므로, "Reader App이니까 외부 결제로 자유롭게 유도할 수 있다"는 이해는 잘못입니다.

비디오, 음악, 서적 같은 소비형 콘텐츠와 앱 기능이나 게임 내 화폐는 취급이 다릅니다. 내 앱이 어느 카테고리에 속하는지를 흐리게 둔 채 제출하면 판단이 흔들리기 쉽습니다.

애매하다면 "팔고 있는 것이 콘텐츠인지 기능인지", "구매 동선이 어디에 있는지"를 정리하고, 스토어 설명과 심사 코멘트에서도 보충 설명하는 편이 안전합니다.

실무적으로는 제출 전에 다음 3가지를 Yes / No로 확인하면 판단이 쉬워집니다.

  • 주 기능이 정말 잡지, 신문, 서적, 오디오, 음악, 비디오 제공인가
  • 외부에서 구매한 콘텐츠를 열람하게 하는 구조인가
  • iOS 쪽에서 IAP를 제공하지 않는가

이 셋 중 하나라도 애매하면 Reader App 예외를 전제로 설계하지 않는 편이 더 안전합니다.


카테고리 4: 디자인과 경험 품질

11. 플랫폼 기대에서 크게 벗어난 UI

완전히 독자적인 내비게이션 체계, 뒤로 가기 동선 부재, 읽기 어려운 대비 등은 사용성 평가를 떨어뜨립니다. 심사에서는 외형의 독창성보다 플랫폼 기대에 맞는 조작성 우선이 더 유리합니다.

12. WebView로 감싼 것처럼만 보인다

사실상 웹사이트를 그대로 보여주는 앱은 네이티브 경험으로서의 가치가 약하다고 판단될 수 있습니다.

위험한 패턴:

  • 거의 전체 화면이 WebView다
  • 네이티브 고유 기능이 없다
  • 오프라인일 때 아무것도 할 수 없다
  • 기기 연동이나 알림 등 앱만의 가치가 없다

단순 래퍼처럼 보이지 않으려면 공유, 알림, 카메라, 로컬 저장, 오프라인 UX 등 기기 통합의 가치를 분명히 하는 편이 좋습니다.

WebView 자체가 악인 것은 아닙니다. 문제는 네이티브 앱으로 제출했는데 안쪽이 사실상 웹사이트 그대로처럼 보이는 것입니다. WebView를 쓴다면 최소한 다음 중 하나에서는 네이티브 측 가치를 보여주는 편이 좋습니다.

  • 네이티브 내비게이션이나 설정 화면
  • 공유, 알림, 카메라, 파일 저장 같은 기기 기능
  • 오프라인 안내나 캐시 표시
  • 웹 쪽에는 없는 앱 전용 조작 경험

13. 광고나 결제 동선이 과하다

콘텐츠보다 광고가 더 눈에 띄거나, 오탭을 유도하는 배치이거나, 닫기 어려운 interstitial 을 과하게 쓰면 심사뿐 아니라 평점 하락에도 직결됩니다. 수익화 동선이 존재하는 것은 괜찮지만, 그것이 경험을 망가뜨리지 않는지를 실기기에서 다시 봐야 합니다.


카테고리 5: 스토어 메타데이터와 신고 정보

심사에서는 앱 본체뿐 아니라 스토어 설명문과 신고 내용도 함께 봅니다. 이 부분이 실제와 어긋나면 구현이 맞아도 막힙니다.

다시 봐야 할 항목:

  • 스크린샷
  • 앱 설명문
  • 부제목
  • 연령 등급
  • App Privacy / Data safety
  • 광고 포함 여부 신고

특히 위험한 것은 아직 구현하지 않은 기능을 설명문에 써 두는 것입니다. 앞으로 구현할 예정인 기능을 미리 올려 두는 것도 피하는 편이 안전합니다.


카테고리 6: UGC와 고위험 카테고리

14. UGC가 있는데 moderation 동선이 없다

사용자 생성 콘텐츠를 다루는 경우, 게시 기능만 있다고 충분하지 않습니다. 최소한 다음 동선은 준비해 두는 편이 좋습니다.

  • 신고 기능
  • 차단 기능
  • 이용약관
  • 문의 창구
  • 위반 콘텐츠 대응 방침

UGC가 있는데 방어 장치가 보이지 않으면 심사 측은 운영 리스크를 걱정하기 쉽습니다.

15. 건강 / 금융 / 어린이 대상 카테고리에서 설명 책임이 부족하다

의료 / 건강, 금융 / 투자, 어린이 대상, 상시 위치 정보 앱은 일반 카테고리보다 훨씬 엄격하게 봅니다. 정확성, 법규 준수, 오해의 여지가 없는 설명, 문의처 명시 등 더 높은 기준을 전제로 두는 편이 좋습니다.

특히 이런 카테고리에서는 "편리해 보여서 넣은 설명문"이 오히려 역효과가 될 수 있습니다. 건강이나 투자 관련 표현은 효과 보장, 오인, 과도한 기대를 부르는 방향을 피해야 합니다.

실무적으로는 다음 관점으로 다시 보면 사고를 줄일 수 있습니다.

  • 의료 / 건강: 진단이나 치료를 단정하고 있지 않은가
  • 금융: 수익 보장이나 과도한 기대를 부추기지 않는가
  • 어린이 대상: 외부 링크, 광고, 결제 동선이 너무 강하지 않은가
  • 상시 위치 정보: 상시 수집이 정말 필요한가, 대안은 없는가

이 카테고리에서는 기능 추가보다 "과하게 말하고 있지 않은가", "설명 책임을 다하고 있는가"를 확인하는 쪽이 더 중요합니다.


자주 나오는 리젝 문구 해석법

심사 메시지는 짧고 추상적인 경우가 많아서, 그대로 읽으면 무엇을 고쳐야 할지 알기 어렵습니다. 실제로는 다음처럼 읽어 바꾸면 움직이기 쉬워집니다.

심사 메시지 경향 실제로 의심받는 것 먼저 해야 할 일
App Completeness 미완성, 크래시, 끊긴 동선 실행 영상을 찍고 눌리지 않는 버튼이나 빈 화면을 찾는다
Your app requests access... 불필요한 권한, 설명 부족 그 권한이 필요한 화면과 설명문을 1:1로 대응시킨다
Metadata does not reflect... 스크린샷이나 설명문 불일치 스토어 설명, 이미지, 구현 차이를 나란히 비교한다
Use in-app purchase 디지털 결제를 외부로 유도 무엇을 파는지 정리하고 IAP 전제로 되돌린다
Target API level Android 제출 요건 미달 target SDK와 의존 라이브러리를 업데이트하고 재시험한다
Data safety form is inaccurate 신고와 SDK 구현의 차이 SDK를 목록화하고 Console 신고를 갱신한다

중요한 점은 심사 문구를 사양서처럼 읽는 것입니다. 추상적으로 보여도 대부분은 미완성, 설명 부족, 신고 불일치 중 하나로 분류할 수 있습니다.


Google Play에서 놓치기 쉬운 항목

신규 personal account의 첫 공개 요건

처음 Google Play에 내는 독자라면 이 부분이 특히 중요합니다. 새 personal developer account에는 앱 내용과 별개로 production 공개로 넘어가기 위한 account 요건이 있습니다.

대표적인 것은 다음 두 가지입니다.

  • closed test에 12명 이상의 tester 가 지속적으로 참여하고 있어야 한다
  • Play Console mobile app을 이용한 Android 실기기 device verification 을 완료해야 한다

이것은 전형적인 "리젝 메일"이라기보다 Play Console 상에서 production으로 넘어가지 못하게 막는 블록에 가깝습니다. 즉, 앱이 완성되어 있어도 account 측 조건을 만족하지 못하면 공개할 수 없습니다.

첫 공개에서 자주 막히는 포인트는 다음과 같습니다.

  • tester 수는 충분하지만 14일 연속 opt-in 조건을 만족하지 못했다
  • tester 가 참여만 했고 실제 사용이나 피드백은 부족하다
  • account owner 가 실기기 verification 을 끝내지 않았다
  • internal test만으로 충분하다고 생각해 closed test 조건을 놓친다

첫 공개라면 구현 확인과 병행해서 이 요건을 가장 먼저 처리해 두는 편이 안전합니다.

target SDK 추종 누락

Google Play의 target API level 요구사항은 계속 갱신됩니다. 작성 시점의 숫자가 아니라 제출 시점의 최신 요구사항을 Play Console에서 확인하는 운영 습관이 필요합니다.

여기서 헷갈리기 쉬운 것은 targetSdkVersionminSdkVersion 의 차이입니다.

  • targetSdkVersion: 새 Android 동작에 어디까지 대응하는 전제인지
  • minSdkVersion: 얼마나 오래된 Android까지 지원하는지

minSdkVersion 을 올리지 않았더라도 Google Play 제출 요건 관점에서는 targetSdkVersion 이 부족하기만 해도 막힐 수 있습니다.

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        targetSdkVersion rootProject.ext.targetSdkVersion
        minSdkVersion 24
    }
}

대응의 핵심은 숫자만 바꾸고 끝내지 않는 것입니다. target SDK를 올리면 권한 모델, 백그라운드 제한, 알림 동작, foreground service, 파일 접근 등이 바뀔 수 있습니다. 업데이트 시에는 다음을 함께 확인합니다.

  • Android Gradle Plugin과 의존 라이브러리의 대응 상태
  • 권한 다이얼로그 동작 차이
  • 알림과 백그라운드 처리 제한
  • 주요 타깃 기기에서의 실기기 테스트

위험 권한이 manifest에 남아 있다

사용하지 않는 위험 권한이 manifest에 남아 있으면 그 자체로 설명 책임이 생깁니다. 특히 CONTACTS, CALL_LOG, SMS, READ_PHONE_STATE 등은 신중하게 다시 봐야 합니다.

<!-- 사용하지 않는 권한은 선언하지 않는다 -->
<!-- <uses-permission android:name="android.permission.READ_CONTACTS" /> -->
<!-- <uses-permission android:name="android.permission.READ_CALL_LOG" /> -->

Data safety와 구현이 일치하지 않는다

Analytics SDK나 광고 SDK를 추가했는데 Console 측 신고가 오래된 채로 남아 있는 경우는 생각보다 흔합니다. SDK를 바꿀 때마다 Data safety도 같이 갱신해야 할 항목으로 다루는 운영이 필요합니다.

Data safety 는 "이 앱이 어떤 데이터를 수집 / 공유하고, 무엇에 쓰는가"를 Google Play에 신고하는 영역입니다. Apple 쪽 App Privacy 도 발상은 비슷하고, 구현과 어긋나면 신뢰를 떨어뜨립니다.

가장 안전한 해결책은 감으로 쓰지 말고 목록화하는 것입니다.

  • 들어가 있는 SDK를 나열한다
  • 각각이 다루는 데이터 종류를 확인한다
  • 자체 API가 보내는 데이터도 정리한다
  • Play Console / App Store Connect 신고와 대조한다
  • 변경이 있었다면 privacy policy도 함께 갱신한다

권한은 "쓰는 순간"에 요청하기

실행 직후 여러 권한 다이얼로그를 한꺼번에 띄우는 설계는 사용자와 reviewer 모두에게 나쁜 인상을 남기기 쉽습니다.

더 좋은 방침은 다음과 같습니다.

  • 기능에 도달했을 때 권한을 요청한다
  • 직전에 짧은 pre-permission 설명을 넣는다
  • deny 되었을 때의 대체 동선을 준비한다

이것은 심사 대응뿐 아니라 허용률 개선에도 도움이 됩니다.


신청 코멘트에 적어야 할 것

심사 코멘트란은 가볍게 취급되기 쉽지만 reviewer 의 혼란을 줄이는 중요한 공간입니다. 특히 로그인 필수 앱, 권한 사용 앱, 외부 기기 연동 앱에서 효과가 큽니다.

적으면 좋은 내용:

  • 데모 계정 정보
  • 핵심 기능까지 도달하는 절차
  • 권한을 사용하는 화면 설명
  • 2FA나 특정 역할이 필요한 경우의 보충 설명
  • 외부 서버나 기기 의존이 있는 경우의 보충 설명

짧은 템플릿 예시:

심사 계정:
Email: reviewer-demo@example.com
Password: xxxxxxxx

주요 테스트 흐름:
1. 심사 계정으로 로그인
2. 위치 권한을 테스트하기 위해 Map 탭 열기
3. 해지 안내를 확인하기 위해 Settings > Subscription 열기

리젝 이후 대응 방침

리젝 시 중요한 것은 감정적으로 반박하는 것이 아니라, 지적 내용을 정확히 분류하는 것입니다.

진행 순서의 기본은 다음과 같습니다.

  1. 지적 문구를 그대로 다시 읽는다
  2. 재현 조건을 확인한다
  3. 구현 결함, 설명 부족, 신고 실수 중 하나로 분류한다
  4. 필요하면 스크린샷이나 영상으로 보강한다

정말 오판처럼 보여도, 보통은 먼저 설명 부족을 메우는 편이 더 빨리 해결됩니다. 이의 제기는 마지막 수단으로 생각하는 편이 실무적입니다.

실제 답변에서는 긴 변명보다 "무엇을 고쳤는지"를 짧고 명확하게 쓰는 편이 통과되기 쉽습니다.

리뷰 감사합니다.

문제를 [implementation / metadata / clarification] 로 파악했습니다.
다음과 같이 수정했습니다:
1. [issue] 수정
2. [metadata or review notes] 업데이트
3. [account / explanation / screenshot] 추가

검증 방법:
1. 심사 계정으로 로그인
2. [screen name] 열기
3. [action] 탭하기

필요하다면 추가 스크린샷이나 화면 녹화를 제공할 수 있습니다.

한국어로 정리하면 결국 "원인", "수정 내용", "확인 절차" 세 가지만 있으면 충분합니다. 심사 대응이 길어지는 팀일수록 이 세 가지가 뒤섞여 대화가 어려워지기 쉽습니다.

제출 전 48시간에 할 일

출시 직전에는 기능을 하나 더 넣고 싶은 유혹이 강하지만, 심사 통과율을 높이려면 마지막 시간은 구현보다 확인에 쓰는 편이 더 효과적입니다.

48시간 전

  • 스토어 설명문, 스크린샷, 연령 등급, Data safety / App Privacy를 다시 본다
  • 사용 중인 SDK와 권한 목록을 정리한다
  • reviewer 용 계정과 심사 코멘트를 준비한다

24시간 전

  • 최신 iPhone OS, 이전 iOS 한 버전, 여러 Android 기기에서 실행을 확인한다
  • 비행기 모드, 느린 네트워크, 최초 설치 상태를 시험한다
  • 결제, 로그인, 권한 요청, 회원 탈퇴 동선을 끝까지 확인한다

6시간 전

  • 스크린샷과 실제 앱에 차이가 없는지 마지막으로 본다
  • 스토어 신고와 SDK 구성이 어긋나지 않는지 확인한다
  • 심사 코멘트에 도달 절차와 보충 설명을 끝까지 적는다

이 48시간 플로우를 매번 돌리기만 해도 리젝 비율을 꽤 낮추기 쉬워집니다.

재발 방지를 위한 운영

한 번 통과한 뒤에도 기능 추가나 SDK 변경으로 같은 논점이 다시 나타나기 쉽습니다. 개인 개발이든 팀 개발이든 다음 운영을 두면 사고를 줄일 수 있습니다.

  • SDK를 추가했다면 권한, Data safety, App Privacy 갱신을 한 세트로 묶는다
  • 스토어 설명문에는 구현된 기능만 쓴다
  • 새 권한을 추가할 때는 사용 화면과 설명문을 PR에서 함께 확인한다
  • 출시 전 체크리스트를 issue나 템플릿으로 만들어 반복 사용한다
  • 리젝 문구와 대응 내용을 팀 내부에 기록하고 다음 심사 코멘트에 활용한다

심사 대응을 "매번 그 자리에서 새로 생각하는 작업"으로 두지 않는 것이 결국 가장 효과적입니다.


제출 전 체크리스트

제출처별로 그대로 사용할 수 있는 형태로 나누면 누락을 찾기 쉽습니다. 공통 항목도 각각의 표에 포함했습니다.

App Store 제출 전 체크리스트

관점 확인 항목 왜 중요한가
완성도 [ ] 최신 iOS와 이전 주요 버전 한 개에서 첫 실행, 빈 데이터, 느린 네트워크에서도 크래시하지 않는다 App Completeness 와 크래시 지적을 막는다
심사 동선 [ ] reviewer 용 계정, 도달 절차, 필요시 2FA 절차를 심사 코멘트에 적었다 reviewer 가 확인하지 못하는 것만으로 반려될 수 있다
권한 [ ] Info.plist 설명문이 구체적인 기능명과 연결되어 있다 권한 설명 부족을 피한다
개인정보 [ ] Privacy Policy URL이 공개되어 있고 App Store Connect에 설정되어 있다 데이터 수집 앱의 기본 요건
신고 정합성 [ ] App Privacy 신고 내용이 현재 SDK 구성과 일치한다 신고 불일치를 피한다
결제 [ ] 디지털 콘텐츠와 앱 기능 판매는 IAP를 사용한다 외부 결제 유도를 피한다
구독 [ ] 플랜명, 기간, 총 청구액, 자동 갱신, 무료 체험 후 가격, 해지 방법을 구독 화면에서 읽을 수 있다 구독 설명 부족을 피한다
구독자 동선 [ ] Terms of Service / Privacy Policy 링크와 restore purchases 또는 sign in 동선이 있다 기존 구독자 동선 부족을 피한다
ATT [ ] IDFA나 tracking 을 사용하는 경우 ATT와 App Privacy가 맞춰져 있다 Tracking 관련 반려를 막는다
메타데이터 [ ] 스크린샷, 설명문, 부제목이 현재 구현과 일치한다 metadata mismatch 를 피한다

Google Play 제출 전 체크리스트

관점 확인 항목 왜 중요한가
첫 공개 요건 [ ] 새로운 personal account 인 경우 12명 이상, 14일 이상의 closed test 조건을 충족했다 production access 로 못 넘어가는 블로커를 피한다
계정 확인 [ ] 새로운 developer account 인 경우 account owner 가 Android 실기기 device verification 을 완료했다 공개 전 account gate 를 피한다
완성도 [ ] 여러 Android 기기에서 첫 실행, 오프라인, 빈 데이터 시 동작을 확인했다 구현 결함과 pre-review check 탐지를 줄인다
기술 요구 [ ] targetSdkVersion 이 제출 시점의 요구를 만족한다 제출 요건 미달을 피한다
배포 설정 [ ] 64-bit 대응을 포함한 빌드 설정과 의존 라이브러리 호환성을 확인했다 배포 시 기술 요건 위반을 피한다
권한 [ ] 위험 권한은 정말 필요한 것만 남기고 필요성을 설명할 수 있다 permissions 와 기능의 불일치를 피한다
개인정보 [ ] Privacy Policy 가 공개되어 있고 Play Console 설정과 일치한다 데이터 처리 설명 부족을 피한다
신고 정합성 [ ] Data safety, 광고 신고, 연령 설정이 현재 SDK 구성과 일치한다 Console 측 신고 불일치를 피한다
로그인 동선 [ ] reviewer 가 핵심 기능에 도달할 수 있는 계정이나 절차를 준비했다 실기기 확인 불가로 인한 반려를 피한다
결제 [ ] 정기 구독이나 결제 동선의 가격, 갱신 조건, 해지 방법이 이해하기 쉽다 오인을 부르는 결제 UI를 피한다
스토어 정보 [ ] 스토어 설명문, 스크린샷, 카테고리 설정이 구현과 일치한다 listing mismatch 를 피한다
SDK 운영 [ ] SDK 추가 / 제거 차이가 Data safety 와 permissions 에 반영되어 있다 뒤늦은 수정 요청을 피한다

정리

앱 심사에서 자주 막히는 것은 단순한 버그보다 구현과 설명의 어긋남 그리고 운영 준비 부족입니다.

특히 중요한 것은 다음 네 가지입니다.

  • reviewer 가 헷갈리지 않고 핵심 기능을 확인할 수 있어야 한다
  • 권한과 데이터 수집 이유가 구체적으로 설명되어 있어야 한다
  • 결제 규칙과 스토어 신고가 올바르게 정리되어 있어야 한다
  • 스토어 메타데이터와 구현이 일치해야 한다

출시가 가까워질수록 기능을 더 넣고 싶어지지만, 심사 통과율을 높이려면 마지막 하루를 체크리스트 소화에 쓰는 편이 더 효과적입니다.


참고 링크