App Store 첫 등록은 CI/CD가 있어도 절반은 수동이었다

CI/CD로 TestFlight 업로드까지 자동화해두고, App Store 첫 등록도 명령어 몇 줄이면 끝날 줄 알았다. 그런데 App ID를 만드는 첫 단계부터 fastlane produce가 막혔고, 결국 웹 UI에서 하나씩 눌러 등록했다. 자동화가 어디서 멈추는지, 그리고 준비 단계에서 나를 붙잡은 사소한 오류들을 순서대로 남긴다

기존 팀 인증서를 재사용하지 않고 새 계정으로 갔다

이 앱을 처음 배포했던 사람은 현재 프로젝트와 무관한 별개의 인물이었다. 기존 팀(29DG16BN93)의 인증서가 살아 있으니 그걸 재사용하는 선택지도 있었다. 하지만 그 팀에 소속되면 계정 권한과 인증서 관리 주체가 남의 것에 묶인다. 그래서 완전히 새로운 Apple ID로 Apple Developer Program에 개인 자격으로 신규 가입했다

가입 직후 계정 상태는 “대기 중”으로 표시됐다. Apple 안내로는 활성화까지 최대 48시간이 걸릴 수 있다고 했지만, 실제로는 그보다 빨리 열렸다. 활성화 후 새 팀 ID(FWVZ1G98J6)를 발급받았고, 이 값을 서명·배포 파이프라인 전체에 반영했다

fastlane produce로 App ID를 만들려다 막혔다

App ID 생성은 fastlane produce로 자동화할 수 있다고 알려져 있어 먼저 시도했다. 결과는 실패였다. App Store Connect API 키만으로는 안 되고, 레거시 Apple ID 세션 로그인이 필요한 코드 경로를 타면서 비대화형 환경에서 막혔다

[!] No value found for 'username'
Missing password for user ... non-interactive shell

CI 환경처럼 사람이 붙어 있지 않은 셸에서는 이 로그인 프롬프트를 넘길 방법이 없었다. 우회하는 데 시간을 더 쓰기보다, App ID 생성과 앱 레코드 생성은 웹 UI에서 1회만 수동으로 처리하기로 했다. 첫 등록은 한 번뿐이라 자동화의 이득이 크지 않다는 판단이었다

App ID 설명 필드의 밑줄 하나가 막았다

Developer Portal에서 App ID를 수동 등록했다. 번들 ID는 com.yeok.anyThing으로 문제없이 들어갔다. 그런데 설명(Description) 필드에 any_thing 입력하니 오류가 났다

Invalid description

원인은 밑줄(_)이었다. 하이픈으로 바꾸니 바로 통과했다.

any_thing   → Invalid description
any-thing   → 통과

Apple 문서 어디에도 “밑줄 금지”라고 눈에 띄게 적혀 있지 않다. 이런 건 검색보다 직접 부딪혀야 알게 되는 종류다. 입력값이 거부되면 특수문자부터 의심하는 게 시간을 아낀다

앱 이름이 이미 선점돼 있었다

App Store Connect에서 앱 레코드를 만들 때 이름 “뭐지?”가 이미 사용 중이라는 충돌이 났다. App Store 앱 이름은 전역에서 고유해야 한다. Play Store에서 쓰던 이름이라고 그대로 통과하지 않는다. 부제를 붙여 “뭐지? – 아무것이나”으로 확정했다. SKU는 any-thing-ios, 내부 앱 고유번호(Apple ID)는 1234567890로 발급됐다. 이름 충돌은 첫 등록에서 흔히 만나는 지점이라, 원하는 이름이 선점됐을 경우의 대안을 미리 하나 정해두면 흐름이 끊기지 않는다

스크린샷은 6.9가 아니라 6.5가 필수였다

스크린샷은 iOS 시뮬레이터에서 앱을 직접 실행해 캡처했다. 여기서 두 가지를 먼저 처리해야 했다

첫째, AdMob 테스트 광고다. 시뮬레이터에서는 실제 광고 대신 “Test mode” 워터마크가 붙은 임의의 테스트 광고가 뜬다. 하단 광고 영역을 앱 배경색으로 채워 지웠다. 둘째, Flutter의 DEBUG 리본이다. iOS 시뮬레이터는 release/profile 모드 실행을 지원하지 않아 debug 모드로만 돌릴 수 있었고, 우상단 대각선 “DEBUG” 배너가 그대로 찍혔다. 같은 방식으로 배경색을 채워 처리했다

규격에서 한 번 헷갈렸다. 최신 아이폰이 6.9인치라 그 규격을 준비하면 될 줄 알았는데, App Store Connect의 “미디어 관리 → 모든 크기 보기”에서 확인하니 필수 슬롯이 달랐다

iPhone 필수: 6.5 디스플레이 (1242×2688 등)  ← 이게 필수
iPhone 선택: 6.9 디스플레이                 ← 접혀 있음(선택)
iPad  필수: 13 디스플레이 (2064×2752 등)

시뮬레이터 원본 해상도(1320×2868)를 6.5인치 규격(1242×2688)으로 리사이즈해 업로드했다. 최종적으로 iPhone 6.5인치 3장, iPad 13인치 2장을 올렸다. 어떤 크기를 준비할지는 기기 세대로 추측하지 말고, App Store Connect가 “필수”로 표시한 슬롯을 먼저 열어보는 게 정확하다

마무리

이 삽질 중 일부는 다음 빌드부터 사라진다. 예를 들어 App ID나 앱 레코드는 한 번 만들면 끝이고, 이름 충돌도 두 번 겪지 않는다. 첫 등록에서만 겪는 일회성 비용이라는 뜻이다. 반대로 스크린샷 규격 확인처럼 버전마다 반복되는 것도 있다. 한 줄로 요약하면, 첫 App Store 등록에서 CI/CD가 자동화하는 범위는 빌드·업로드까지고, App ID 생성부터 앞단은 웹 UI 수동 작업이다. 첫 등록을 앞두고 있다면, 자동화로 밀어붙이기 전에 이 세 가지를 먼저 확인하는 것을 권한다. 팀을 새로 팔지 기존 팀에 얹을지, 원하는 앱 이름이 선점됐는지, 그리고 스크린샷은 기기 세대가 아니라 App Store Connect가 표시하는 필수 슬롯 기준으로 준비하는지다