App Store Connect API 키: Team Key로 할 수 있는 것과 없는 것
새 앱을 App Store Connect에 등록하면서, 화면을 계속 왔다 갔다 하기 귀찮아서 API로 자동화할 수 있는 부분은 자동화해보려고 했습니다. 결론부터 적으면 절반만 성공했습니다. 조회·수정은 되는데, 앱 생성 자체는 API로 안 됩니다.
인증부터
App Store Connect API는 OAuth가 아니라 자체 서명한 JWT로 인증합니다. 필요한 건 셋입니다.
- Key ID: 키를 만들면 발급되는 10자리 식별자 (예:
AuthKey_XXXXXXXXXX.p8파일명에 들어있음) - Issuer ID: 계정 전체에 하나, UUID 형태. App Store Connect → 사용자 및 접근 → 통합 탭 상단에 있습니다
- .p8 개인키 파일: 생성 시점에 딱 한 번만 다운로드할 수 있습니다. 잃어버리면 키를 새로 만들어야 합니다
Key ID·Issuer ID는 식별자일 뿐 비밀이 아닙니다(OAuth의 Client ID 같은 개념). 보호해야 하는 건 .p8 파일 하나뿐입니다.
JWT는 직접 서명하면 됩니다. Node 기준으로 외부 라이브러리 없이 crypto.sign만으로 됩니다. ES256 알고리즘이라 dsaEncoding: 'ieee-p1363' 옵션을 꼭 줘야 합니다(안 주면 Node 기본값인 DER 인코딩으로 서명되는데, Apple은 raw R||S 형식을 기대합니다).
const signature = crypto.sign('sha256', Buffer.from(signingInput), {
key: privateKeyPem,
dsaEncoding: 'ieee-p1363',
});팀 키와 개별 키
App Store Connect API 키는 두 종류입니다.
- 팀 키(Team Key): 조직 계정 자체에 속합니다. 역할을 직접 지정하고, 만든 사람이 팀에서 빠져도 유지됩니다. CI·자동화용입니다. 계정당 최대 50개까지 활성 상태로 둘 수 있습니다.
- 개별 키(Individual Key): 특정 사람 계정에 묶입니다. 그 사람이 팀에서 가진 권한을 그대로 상속받고, 1인당 1개만 만들 수 있습니다.
자동화라면 당연히 팀 키입니다.
역할을 올려도 안 되는 것
여기서 한참 헤맸습니다. 처음 쓰던 키가 "앱 관리(App Manager)" 역할이었는데, 새 앱을 만들려고 POST /v1/apps를 호출하니 이런 에러가 났습니다.
{
"code": "FORBIDDEN_ERROR",
"detail": "The resource 'apps' does not allow 'CREATE'. Allowed operations are: GET_COLLECTION, GET_INSTANCE, UPDATE"
}권한이 부족한가 싶어서 "관리자(Admin)" 역할로 새 키를 만들어 다시 시도했습니다. 똑같은 에러였습니다. 혹시나 해서 세 번째 키까지 만들어봤는데 결과는 같았습니다.
세 번 다 같은 응답이 나오고 나서야 이게 권한 문제가 아니라는 걸 알았습니다. App Store Connect의 공개 API는 앱 생성을 아예 지원하지 않습니다. 역할을 아무리 올려도 apps 리소스의 CREATE는 원천적으로 막혀 있고, 허용되는 건 조회(GET_COLLECTION, GET_INSTANCE)와 수정(UPDATE)뿐입니다. Fastlane의 produce나 다른 CI 도구들이 앱 생성을 자동화할 때 공개 API 대신 Apple ID 로그인 세션(2단계 인증 포함)을 흉내 내는 비공식 방식을 쓰는 이유가 이거였습니다.
반대로 Bundle ID(App ID) 등록은 공개 API로 됩니다. POST /v1/bundleIds는 정상적으로 201을 반환합니다. 그래서 "번들 ID는 미리 등록해두고, 실제 앱 레코드 생성만 웹 화면에서 하는" 순서로 정리했습니다.
// 되는 것
POST /v1/bundleIds // 201
// 안 되는 것 (역할 무관)
POST /v1/apps // 403 FORBIDDEN_ERROR이름 필드에서 한 번 더 걸렸습니다
Bundle ID를 등록할 때 name에 한글을 넣었더니 409 ENTITY_ERROR.ATTRIBUTE.INVALID가 났습니다. 이 필드는 Apple 내부에서 쓰는 식별용 라벨이라 영문/숫자만 허용하고, 스토어에 노출되는 실제 앱 이름과는 다른 값입니다. 앱 레코드 자체의 name(App Store에 노출되는 이름)은 한글이 됩니다. 같은 "이름"이라는 단어를 쓰는 필드 두 개가 서로 다른 규칙을 가진 셈이라 처음엔 헷갈렸습니다.
앱 단위 접근 범위도 따로 있었습니다
앱을 웹 화면에서 만들 때 "사용자 액세스 권한"을 묻는 단계가 있습니다. 제한된 액세스를 고르면 그 자리에서 지정한 사람·역할만 이 앱을 볼 수 있고, 이미 "전체 앱" 범위로 설정된 키라도 이 목록에 없으면 안 보입니다. 전체 액세스를 고르면 "전체 앱" 범위를 가진 역할·키가 자동으로 이 앱에 접근하게 됩니다.
키 쪽의 "앱 관리" 역할 + "전체 앱" 범위 설정과, 앱 쪽의 "전체 액세스" 설정이 둘 다 맞아야 자동화 키가 새로 만든 앱을 조회할 수 있습니다. 하나라도 어긋나면 목록 조회에서 그 앱만 조용히 빠집니다. 에러가 나는 게 아니라 그냥 안 보이니, 처음엔 "아직 전파가 안 됐나" 하고 몇 번 더 조회해봤습니다.
정리
| 하려는 것 | 공개 API | 필요 조건 |
|---|---|---|
| 앱 목록/상세 조회 | 가능 | 팀 키, 최소 조회 권한 |
| Bundle ID 등록 | 가능 | POST /v1/bundleIds |
| 앱 레코드 생성 | 불가 | 역할 무관, 웹 UI(또는 비공식 세션 인증)만 가능 |
| 새로 만든 앱 조회 | 조건부 | 키 범위 "전체 앱" + 앱 자체 "전체 액세스" 둘 다 필요 |
키를 세 개나 만들어보고서야 확인한 사실이라, 이 글이 같은 삽질을 한 번 줄여주면 좋겠습니다.
함께 읽기
- RN 앱 두 번째 배포: 재사용되는 인증서, 새로 해야 하는 등록몇 주 전에 만든 고객 포털 앱을 반쯤 복사해서 새 RN 앱을 하나 더 만들었습니다. 성경 속 여정을 지도에 그리는 화면 하나를 웹에서 옮겨온 것뿐인데, 옮기고 나니 "인증서까지 그대로 가져다 쓸 수 있나"가 궁금해졌습니다. 결론부터 적으면 절반만 맞았습니다.
- Expo 앱 스토어 배포 준비 체크리스트: 순서가 중요한 이유새 Expo 앱 하나를 두 스토어에 올릴 준비를 했습니다. 필요한 항목 자체는 검색하면 다 나옵니다. 그런데 정작 저를 막은 건 항목이 아니라 순서였습니다. 앞 단계가 끝나야 뒤 단계의 값이 생기는 구간이 몇 군데 있고, 그걸 모르고 덤비면 "왜 이 필드가 비어 있지"에서 멈춥니다.
- App Store Connect와 Google Play API 비교: 인증·권한·한계앱 하나를 두 스토어에 등록하면서 양쪽 API를 같은 날 연달아 만졌습니다. 목적이 똑같았기 때문에 차이가 잘 보였습니다. 인증 방식부터 다르고, 권한을 다루는 사고방식이 다르고, 심지어 "안 되는 걸 알려주는 방식"까지 달랐습니다.
- Expo 앱은 만들었는데 출시가 안 됩니다: `eas build`보다 먼저 준비할 것들Expo를 사용하면 네이티브 빌드 과정이 크게 단순해집니다. 하지만 앱스토어 출시까지 명령어 한두 줄로 끝나는 것은 아닙니다. 실제 자동 배포를 만들려면 앱 등록, 서명 자격 증명, 스토어 API 권한, CI/CD 승인 절차가 먼저 준비되어야 합니다.
- 같은 Expo 앱인데 배포 방식은 달랐습니다: iOS와 Android 심사 제출 비교기앞선 글에서는 이미 TestFlight에 올라간 iOS 빌드를 AI가 App Store 심사까지 제출한 과정을 다뤘습니다.