전체 글
약 3분

첫 앱 출시기 4 — 업로드는 성공했고 스크린샷 55장은 빠졌다

11개 언어의 스토어 자료를 준비했지만 실제로 올라간 것은 기존 한국어 등록정보뿐이었습니다. 잘못된 폴더 경로를 발견하고, 서명·권한·업로드 결과를 각각 확인한 기록입니다.

#android #play-store #출시기 #gradle #개인개발

들어가며

11개 언어에 스크린샷을 5장씩 준비했습니다. 모두 55장이었습니다. 빌드와 업로드도 성공했는데, Play 콘솔에 올라간 것은 스크린샷 3장과 옛 문구가 담긴 한국어 등록정보뿐이었습니다.

이 글을 쓰면서 등록정보 폴더가 잘못됐다는 것을 발견했습니다. 파일은 app/play/에 만들어 뒀는데, Gradle Play Publisher가 읽는 곳은 app/src/main/play/였습니다. 준비한 55장은 업로드 대상에 들어가지도 않았습니다.

광고와 결제를 붙인 뒤 업로드를 준비하는 데 이틀을 썼습니다. 서명, 버전 번호, 서비스 계정 권한을 정하고 등록정보를 만드는 과정이었습니다. 2026년 8월 22일인 지금은 테스트 트랙까지 올린 상태입니다.

스토어에 올릴 자료부터

제목, 짧은 설명, 긴 설명과 함께 512×512 아이콘, 1024×500 피처 그래픽을 준비했습니다. 스크린샷 55장은 에뮬레이터에서 자동으로 찍었습니다.

좌표를 고정해 누르면 언어별 글자 길이에 따라 버튼이 밀릴 수 있었습니다. 그래서 uiautomator로 요소를 찾아 조작하게 했습니다. 독일어에서도 같은 기능의 화면을 찍기 위한 방식이었습니다.

개인정보 처리방침은 웹에 게시하고, Play의 데이터 보안 양식도 작성했습니다. 계산 기록을 서버로 보내지 않더라도 광고 SDK가 처리하는 데이터는 별도로 확인해야 했습니다. 광고 포함 여부, 타겟 연령층, 콘텐츠 등급도 앱 콘텐츠 설문에 적었습니다.

설정 화면에는 Pretendard의 SIL OFL 1.1 고지와, 투자 자문을 제공하지 않는 금융 계산 도구라는 설명을 넣었습니다. 둘 다 배포 전에 빠지지 않도록 테스트에 포함했습니다.

내가 보관할 키는 업로드 키

Play 앱 서명을 사용해 최종 앱 서명 키는 구글에 맡겼습니다. 직접 보관할 것은 번들을 올릴 권한을 확인하는 업로드 키였습니다.

직접 최종 서명 키를 관리하다 잃으면 같은 앱을 업데이트할 수 없습니다. 업로드 키는 분실하더라도 콘솔에서 재설정을 요청할 수 있습니다. 재설정에 며칠이 걸리는 것으로 확인했으므로, 요청 절차에 의존하지 않도록 별도 백업도 필요했습니다.

val keystoreProperties = rootProject.file("keystore.properties")
if (keystoreProperties.exists()) {
    val props = Properties().apply { keystoreProperties.inputStream().use { load(it) } }
    signingConfigs {
        create("release") {
            storeFile = rootProject.file(props.getProperty("storeFile"))
            storePassword = props.getProperty("storePassword")
            …
        }
    }
}

키와 비밀번호는 git에 넣지 않았습니다. keystore.properties가 없는 기계나 CI에서는 서명 없이 빌드하고, 서명에 필요한 값은 해당 환경에 따로 제공하도록 했습니다.

되돌릴 수 있는 번호와 없는 번호

시험 삼아 처음 올린 번들에서 versionCode = 1을 사용했습니다. 그 번호를 다시 쓸 수 없어 다음 업로드는 2부터 시작했습니다.

항목역할이후 업로드에서 변경할 때
versionCodePlay가 구분하는 정수한 번 사용한 번호를 다시 쓸 수 없음
versionName사용자에게 보이는 버전 문자열이전 표시 버전으로 되돌릴 수 있음
// versionCode 1은 예전 번들이 Play 콘솔에 먹었습니다 — 콘솔은 한 번 본 코드를 다시 받지 않아요
versionCode = 6
versionName = "1.0.0"

테스트 트랙에 세 번 올리는 동안 versionName은 1.0에서 1.3까지 바뀌었습니다. 공개판은 아직 내지 않았으므로 표시 버전을 1.0.0으로 되돌렸습니다. versionCode는 계속 증가시켰습니다.

타겟 API도 올렸습니다. 당시에는 2026-08-31을 마감으로 잡고 API 36을 요구 수준으로 준비했습니다.

Chore: targetSdk 를 36 으로 올린다 — Play 마감 2026-08-31

targetSdk 변경에 따라 권한이나 백그라운드 제한 같은 동작도 달라질 수 있어서, 번호만 바꾸고 끝낼 수는 없었습니다. 출시 직전에 한꺼번에 확인하기에는 부담이 있었습니다.

키를 다시 만들어도 해결되지 않은 오류

Gradle Play Publisher로 번들과 등록정보를 올리게 했습니다.

./gradlew publishReleaseBundle                       # 내부 테스트
./gradlew publishReleaseBundle --track production     # 프로덕션
./gradlew publishReleaseListing                       # 등록정보·스크린샷

Google Cloud에서 서비스 계정 키를 만들고 Play 콘솔에서 권한을 줬습니다. 여기서 두 오류를 만났습니다.

accessNotConfigured는 Cloud 프로젝트에서 Google Play Android Developer API를 켜지 않아 발생했습니다. 당시 이 API를 켜는 데 별도 비용은 없었고 1분 정도면 됐지만, 원인을 찾는 데 30분이 걸렸습니다.

The caller does not have permission은 Play 콘솔의 ‘사용자 및 권한’에서 서비스 계정에 앱 접근과 출시 권한을 주어 해결했습니다. Cloud IAM 역할을 바꾸는 것으로는 해결되지 않았습니다.

처음에는 둘 다 키 문제라고 보고 키를 여러 번 다시 만들었습니다. 실제로 확인할 곳은 API 활성화와 Play 콘솔 권한이었습니다.

업로드할 파일과 올릴 트랙을 확인하기

권한 문제를 해결하고 업로드가 성공한 뒤에도, 맨 앞의 스크린샷 누락은 남았습니다. 플러그인이 어느 폴더에서 파일을 모으는지까지 확인해야 했습니다.

./gradlew generateReleasePlayResources
# → app/build/generated/gpp/release/play/ 아래에 실제로 올라갈 것이 모입니다

여기서 준비한 로케일과 스크린샷이 실제 업로드 대상에 들어갔는지 볼 수 있습니다. 원본 폴더만 확인하고 넘어간 부분이었습니다.

기본 업로드 트랙은 내부 테스트로 뒀습니다. 당시 내부 테스트는 최대 100명을 대상으로 하고 심사 대기가 거의 없어 개발 중 사용하기 좋았습니다. 지정한 테스터에게 배포하는 비공개 테스트, 누구나 참여할 수 있는 공개 테스트, 전체 공개인 프로덕션과 구분했습니다.

play {
    track.set("internal")
    defaultToAppBundles.set(true)
    releaseStatus.set(ReleaseStatus.COMPLETED)
}

프로덕션으로 올릴 때만 --track production을 명시합니다. 기본 명령에는 내부 테스트를 남겼습니다.

마치며

앱을 만드는 데 열흘, 업로드 준비에 이틀이 걸렸습니다. 그 뒤 이 글을 쓰다가 스크린샷 경로가 잘못됐다는 걸 알았습니다. 앱 내부에서 겪은 문제들은 안드로이드를 만들며 배운 것에 따로 남겼습니다.