App Store Connect 업로드: Xcode 로그인 없이 API 키로
아카이브는 만들어졌습니다. 이제 IPA 로 뽑아 올리기만 하면 되는데, 여기서 Xcode 에 애플 계정을 붙이라는 요구가 세 번 나옵니다. 세 번 다 피할 수 있습니다.
손에 있는 것은 배포 인증서와 프로비저닝 프로파일, 그리고 App Store Connect API 키뿐입니다. 그것만으로 끝까지 갔습니다.
수동 서명이라고 적어야 수동이 되는 내보내기
xcodebuild -exportArchive 는 ExportOptions.plist 를 읽습니다. 이 파일에 서명 방식을 적지 않으면 Xcode 가 알아서 하겠다고 나서고, 그러려면 계정이 필요합니다.
<key>method</key><string>app-store-connect</string>
<key>signingStyle</key><string>manual</string>
<key>teamID</key><string>…</string>
<key>signingCertificate</key><string>iPhone Distribution: …</string>
<key>provisioningProfiles</key>
<dict>
<key>번들ID</key><string>프로파일 이름</string>
</dict>provisioningProfiles 는 번들ID 를 열쇠로 두고 프로파일의 이름을 값으로 적습니다. 파일 경로가 아니라 이름입니다. 프로파일 안에 들어 있는 Name 필드를 그대로 써야 하는데, EAS 가 만든 프로파일은 이름이 *[expo] <번들ID> AppStore <타임스탬프> 처럼 생겼습니다. 옮겨 적다 한 글자만 틀려도 못 찾습니다.
security cms -D -i <프로파일> > /tmp/pp.plist
/usr/libexec/PlistBuddy -c "Print :Name" /tmp/pp.plist파일이 있는 것만으로는 안 되는 프로파일
프로파일을 어딘가에 잘 보관해 두었다고 해서 빌드가 찾는 것은 아닙니다. Xcode 는 정해진 폴더만 봅니다.
security cms -D -i <프로파일> > /tmp/pp.plist
UUID=$(/usr/libexec/PlistBuddy -c "Print :UUID" /tmp/pp.plist)
cp <프로파일> ~/Library/MobileDevice/Provisioning\ Profiles/$UUID.mobileprovision파일 이름이 UUID 여야 합니다. 보관본은 보관본대로 두고 이 자리에는 복사본을 넣습니다.
키를 이름으로 찾는 altool
업로드는 xcrun altool 이 하고, 계정 대신 App Store Connect API 키를 받습니다.
xcrun altool --upload-app -f <ipa> -t ios --apiKey <KEY_ID> --apiIssuer <ISSUER_ID>--apiKey 에 넘기는 것은 파일 경로가 아니라 키 ID 입니다. 파일은 정해진 자리에서 정해진 이름으로 찾습니다.
~/.appstoreconnect/private_keys/AuthKey_<KEY_ID>.p8경로를 지정하는 옵션이 없습니다. 그래서 키를 다른 곳에 보관하고 있다면 사본이 하나 더 살게 됩니다. 저는 그 사본을 지우지 않고 두되 권한만 지켜보기로 했습니다. 매 업로드마다 필요한 파일을 매번 복사했다 지우는 것보다, 있는 자리를 알고 600 으로 두는 편이 낫다고 봤습니다.
올리기 전에 하는 검증
altool 에는 업로드하지 않고 같은 검사만 도는 모드가 있습니다.
xcrun altool --validate-app -f <ipa> -t ios --apiKey <KEY_ID> --apiIssuer <ISSUER_ID>VERIFY SUCCEEDED with no errors이걸 먼저 도는 이유는 업로드가 되돌릴 수 없기 때문입니다. 빌드 번호가 소모되고 TestFlight 에 뜹니다. 검증이 통과하면 그다음에 --upload-app 으로 같은 파일을 올립니다.
UPLOAD SUCCEEDED with no errors
Transferred 19786228 bytes in 2.688 seconds계정을 요구하던 세 자리
정리하면 이렇습니다. 내보내기에서 한 번, 서명할 프로파일을 찾을 때 한 번, 업로드에서 한 번입니다. 셋 다 자동으로 두면 Xcode 가 애플 서버에 물어보려 하고, 그러려면 로그인한 계정이 있어야 합니다.
수동으로 바꾸면 대신 내가 알려줘야 할 것이 늘어납니다. 프로파일의 정확한 이름, 그 파일이 있어야 하는 자리, API 키의 이름 규칙. 셋 다 틀리면 조용히 실패하지 않고 그 자리에서 멈추기 때문에 찾기는 어렵지 않았습니다.
이 흐름은 클라우드 빌드를 떠나면서 필요해진 것입니다. 떠날 때 무엇이 딸려 있었는지는 따로 적었습니다.
함께 읽기
- App Store Connect API 키: Team Key로 할 수 있는 것과 없는 것새 앱을 App Store Connect에 등록하면서, 화면을 계속 왔다 갔다 하기 귀찮아서 API로 자동화할 수 있는 부분은 자동화해보려고 했습니다. 결론부터 적으면 절반만 성공했습니다. 조회·수정은 되는데, 앱 생성 자체는 API로 안 됩니다.
- App Store Connect와 Google Play API 비교: 인증·권한·한계앱 하나를 두 스토어에 등록하면서 양쪽 API를 같은 날 연달아 만졌습니다. 목적이 똑같았기 때문에 차이가 잘 보였습니다. 인증 방식부터 다르고, 권한을 다루는 사고방식이 다르고, 심지어 "안 되는 걸 알려주는 방식"까지 달랐습니다.
- Expo SDK 56 앱을 App Store와 Google Play에 배포하기Expo 앱을 배포한다고 하면 보통 eas build 명령 하나를 떠올립니다. 실제 운영 배포는 빌드, 앱 서명, 스토어 업로드, 베타 테스트, 심사, 공개 출시, OTA 업데이트가 서로 다른 단계입니다. 이 구분을 놓치면 CI가 성공했는데 스토어에는 앱이 없거나, JavaScript 수정이라고 생각해 OTA를 발행했는…
- 쓰지 않는 iOS 권한 문구 때문에 App Store 자동 검사에서 반려되는 이유앱스토어 심사라고 하면 사람이 아이폰을 들고 앱을 하나씩 눌러 보는 장면부터 떠올리기 쉽습니다. 저도 그랬습니다. 그런데 iOS 앱을 제출한 뒤 받은 반려 메시지에는 automated analysis라는 표현이 적혀 있었습니다. 사람 심사관의 판단을 기다리기도 전에 업로드한 바이너리의 권한 문구가 자동 분석에서 걸린 경…
- App Store 4.3 리젝 대응: 리뷰어가 탭 세 번으로 확인할 수 있는 답변서성경 족보 1.0 을 8월 27일에 냈고, 그날 리젝됐습니다. Guideline 4.3, 스팸입니다. 다른 개발자가 제출한 앱과 바이너리·메타데이터·컨셉이 유사하고 차이가 미미하다는 문면이었습니다.