App Store Connect API 요청 제한은 무한 재시도로 해결하면 안 됩니다. 이번 주에는 업로드, 상태 조회, 백그라운드 처리 요청을 분리하고, 지수형 대기와 중복 방지, Webhook 기반 확인으로 배포 흐름을 다시 설계해야 합니다. 이 방식은 바이너리 업로드와 API 상태 관리가 서로 막히는 원격 Mac 환경에 특히 적합합니다.
이 글은 원격 Mac에서 TestFlight 빌드를 자동 업로드하는 독립 개발자, 여러 앱을 운영하는 소규모 팀, fastlane이나 자체 스크립트를 유지하는 자동화 담당자를 위한 글입니다. 단순한 JWT 인증 실패나 Processing 단일 장애가 아니라, 요청 과다와 반복 작업으로 전체 배포가 멈춘 경우를 다룹니다.
01제한 초기에 확인할 증거
먼저 “빌드가 보이지 않는다”는 결과만 보고 API 제한이라고 판단하면 안 됩니다. 다음 세 가지 흐름을 분리해서 기록해야 합니다.
- API 요청: HTTP 상태, 응답 본문, 요청 식별자, 응답 헤더
- 파일 업로드: Transporter 또는 Xcode의 전달 결과와 업로드 기록
- 백그라운드 처리: App Store Connect의 Build Processing 상태와 최종 테스트 가능 여부
Apple은 API 요청 제한을 식별하는 방법과 오류 응답 확인 절차를 별도로 안내합니다. HTTP 429가 반환되면 요청 제한 가능성을 우선 조사해야 하지만, 모든 업로드 실패가 429를 의미하는 것은 아닙니다. Apple의 요청 제한 식별 문서와 공식 오류 처리 문서를 함께 확인하십시오.
Transporter가 파일 전달을 완료했다면 바이너리가 전송 단계에 도달했다는 뜻입니다. 그러나 이것만으로 API 조회가 성공했다거나 TestFlight에서 바로 사용할 수 있다는 뜻은 아닙니다. Apple의 빌드 업로드 상태 설명은 업로드와 이후 처리 상태를 구분합니다.
App Store Connect API 반환값이 429일 때는 어떻게 처리해야 합니까?
현재 요청을 즉시 반복하지 말고, 해당 작업의 재시도 상태를 저장한 뒤 대기해야 합니다. 응답 헤더와 본문에 제공된 제한 관련 정보를 먼저 보존하십시오. 공식 문서에 없는 고정 대기 시간이나 요청 한도를 운영 규칙으로 단정하면 안 됩니다. 필요한 경우 대기 후 제한된 횟수만 보상 조회하고, 계속 실패하면 사람에게 넘깁니다.
02반복 조회가 장애를 키우는 구조
가장 흔한 문제는 하나의 Build를 여러 작업이 동시에 조회하는 구조입니다. 업로드 작업, 배포 화면 갱신 작업, 실패 후 재시작한 Runner가 같은 상태를 각각 확인하면 요청 수가 빠르게 늘어납니다.
다음 패턴도 위험합니다.
- 고정 간격으로 상태를 계속 조회합니다.
- 실패한 작업이 이전 상태를 버리지 않고 다시 시작합니다.
- 여러 앱이 하나의 전역 재시도 큐를 공유합니다.
- 업로드 결과 확인과 처리 완료 확인을 같은 함수에서 수행합니다.
- SSH 연결이 끊긴 뒤 새 작업이 기존 작업의 진행 여부를 확인하지 않습니다.
따라서 작업에는 반드시 고유한 request_id, build_id, app_id를 붙이십시오. 예시는 다음처럼 비식별화해야 합니다.
job_id: <JOB_ID>
request_id: <REQUEST_ID>
build_id: <BUILD_ID>
app_id: <APP_ID>
key_id: <KEY_ID>
issuer_id: <ISSUER_ID>
runner: <REMOTE_MAC_HOST>
log: <LOG_PATH>
KEY_ID, ISSUER_ID, 실제 호스트 이름과 로그 경로를 그대로 남기면 자격 증명 구조와 인프라 정보가 노출될 수 있습니다. 로그를 외부에 공유할 때는 반드시 자리표시자로 바꾸십시오.
App Store Connect API는 얼마나 자주 조회해야 합니까?
모든 환경에 적용되는 공식 고정 간격을 임의로 정하면 안 됩니다. 조회 간격은 응답의 제한 정보, 작업 상태, 최근 조회 시각, 동시에 실행 중인 작업 수를 기준으로 계산해야 합니다. 더 중요한 것은 간격 자체보다 중복 조회를 막는 것입니다.
한 Build에는 활성 상태 조회자를 하나만 두십시오. 다른 작업은 저장된 상태를 읽습니다. Webhook 이벤트가 도착하면 그때 필요한 정보만 보완 조회합니다. Apple은 Webhook 설정과 알림 해석 방법을 제공합니다.
03주의: Webhook을 받았다는 사실도 모든 상태가 완료되었다는 뜻은 아닙니다. 이벤트의 종류와 대상 Build를 대조한 뒤, 정말 필요한 경우에만 API로 최종 상태를 확인해야 합니다.
업로드와 처리 확인의 분리
안정적인 원격 Mac 자동 업로드는 다음 네 층으로 나누는 편이 좋습니다.
업로드 실행
Xcode 또는 Transporter가 아카이브와 바이너리를 전달합니다. 여기서는 파일 전달 결과와 업로드 도구 로그를 기록합니다. API 상태 조회를 이 단계의 성공 조건으로 섞지 마십시오. Apple의 빌드 업로드 안내는 업로드 작업의 기본 흐름을 설명합니다.
전달 결과 확인
업로드 도구가 성공 또는 실패를 반환했는지 확인합니다. 성공이면 원격 Mac 작업을 끝내는 것이 아니라, Build 식별자와 업로드 시각을 저장합니다. 실패라면 같은 파일을 무조건 다시 보내지 말고, 전달 결과와 작업 키를 비교합니다.
백그라운드 처리 확인
Apple 서버가 바이너리를 처리하는 동안 Build 상태가 바뀔 수 있습니다. 이 단계에서는 API, App Store Connect 화면, Webhook 이벤트가 서로 다른 시점의 정보를 보여줄 수 있습니다. Build Uploads API 자원 문서를 기준으로 Build 식별자를 보존하십시오.
테스트 가능 상태 확인
최종 조건은 “업로드 성공”이 아니라 테스트 그룹에서 빌드를 선택할 수 있는지입니다. TestFlight 사용 가능 여부를 확인한 뒤에만 배포 작업을 성공으로 표시하십시오.
04제한 이후의 복구 설계
재시도는 실패 종류에 따라 달라야 합니다.
429와 일시적인 서버 응답: 상태를 보존하고 지수형 대기를 적용합니다.- 업로드 도구의 명확한 파일 오류: API 조회를 반복하지 말고 파일과 서명 결과를 점검합니다.
- Build 식별자가 없는 경우: 새 상태 조회를 무한 반복하지 말고 업로드 로그를 확인합니다.
- 이미 전달된 Build: 기존 Build 식별자를 먼저 조회하고 중복 업로드를 막습니다.
- 처리 실패가 확정된 경우: 자동 재시도를 멈추고 새 작업 생성 여부를 사람이 결정합니다.
재시도 횟수와 간격을 예시 코드에 고정값으로 박아 두기보다 설정값으로 분리하십시오. 운영자는 앱별 요청 예산, 동시 실행 수, 마지막 확인 시각, 수동 중지 상태를 볼 수 있어야 합니다. API 오류가 발생하면 Apple의 오류 응답 규칙에 맞춰 상태 코드와 오류 내용을 함께 저장하십시오.
App Store Connect 업로드 제한 뒤에는 바이너리를 다시 올려야 합니까?
항상 그렇지는 않습니다. 파일 전달이 완료되었고 Build 식별자가 확보되었다면 먼저 기존 Build 상태를 확인해야 합니다. 반대로 전달 자체가 실패했거나 식별자를 확인할 수 없다면 업로드 로그를 검토한 뒤 재전송을 결정합니다. 같은 작업을 재실행하기 전에 “이미 전달됨” 상태를 검사하는 단계가 필요합니다.
05원격 Mac 작업의 격리와 관측
원격 Mac을 7일 내내 켜 두는 것보다 중요한 것은 작업을 서로 섞지 않는 것입니다. 앱, 배포 환경, 단계, Runner별로 큐와 상태 파일을 분리하십시오. 하나의 앱이 제한에 걸려도 다른 앱의 업로드가 함께 재시작되지 않아야 합니다.
SSH 연결이 끊겨도 프로세스가 계속 실행되는 구조라면, 재접속 후 새 작업을 만들기 전에 기존 작업 상태를 읽어야 합니다. 상태 파일에는 다음 항목을 남기십시오.
- 업로드 시작과 종료 상태
- 마지막 API 확인 시각
- 최근 응답 상태
- Build 식별자
- Webhook 수신 여부
- 백그라운드 처리 상태
- 사람의 중지 또는 재개 결정
원격 Mac에서 인증서와 API 키를 여러 앱이 공유하면 권한 범위와 폐기 절차가 복잡해집니다. 앱과 환경별로 자격 증명을 나누고, 로그에는 비밀값을 기록하지 마십시오. macOS 전용 도구가 필요한 작업을 지속 운영해야 한다면 원격 Mac 운영 환경을 검토할 수 있습니다.
06실제 TestFlight 경로로 검수하는 체크리스트
수정한 자동화는 모의 응답만으로 끝내면 안 됩니다. 실제로 배포 가능한 테스트 빌드를 한 번 통과시켜야 합니다.
- [ ] 새 작업에
<JOB_ID>와<BUILD_ID>생성 규칙을 적용합니다. - [ ] Xcode 또는 Transporter 업로드 로그를 별도 저장합니다.
- [ ] 전달 완료와 Build 처리 완료를 서로 다른 상태로 기록합니다.
- [ ] 동일 Build를 두 작업이 동시에 조회하지 않는지 확인합니다.
- [ ] 제한 응답이 발생했을 때 무한 재시도가 중지되는지 확인합니다.
- [ ] Webhook 수신 뒤 필요한 보완 조회만 실행되는지 확인합니다.
- [ ] SSH 연결을 끊었다가 다시 접속해도 중복 업로드가 발생하지 않는지 확인합니다.
- [ ] TestFlight에서 해당 빌드를 실제로 선택할 수 있는지 확인합니다.
- [ ] 처리 실패 시 자동 재시도 대신 수동 확인 단계로 넘어가는지 확인합니다.
이 검수에서 업로드는 성공했지만 처리 확인이 계속 실패한다면 두 흐름을 더 분리해야 합니다. 반대로 API 조회만 제한되고 Transporter 전달은 정상이라면, 파일을 다시 올리는 대신 조회 큐를 줄이는 것이 먼저입니다.
07선택 기준과 운영안
| 상황 | 먼저 보존할 증거 | 권장 조치 | 피해야 할 조치 |
|---|---|---|---|
API 요청에 429가 표시됨 |
응답 본문과 헤더 | 지수형 대기, 제한된 보상 조회 | 무한 재시도 |
| 업로드 도구가 실패함 | Transporter 또는 Xcode 로그 | 파일과 서명 단계 점검 | API 조회만 반복 |
| 전달은 끝났지만 Build가 안 보임 | 업로드 결과와 Build 식별자 | 처리 상태를 별도 확인 | 즉시 중복 업로드 |
| 여러 Runner가 같은 Build를 확인함 | 작업 키와 마지막 조회 시각 | 앱별 큐와 단일 조회자 적용 | 전역 재시도 큐 공유 |
| Webhook 이벤트가 도착함 | 이벤트 종류와 대상 Build | 필요한 상태만 보완 조회 | 모든 API 자원 일괄 조회 |
| SSH가 끊겼다가 복구됨 | 원래 작업의 저장 상태 | 기존 작업을 먼저 재개 | 새 업로드 작업 생성 |
현재 자동화가 파일 업로드와 상태 조회를 한 작업으로 묶고 있다면, 먼저 두 체인을 나누십시오. 그 다음 실제 TestFlight 빌드 하나로 중복 업로드, 제한 응답, 처리 완료 확인을 차례로 검증하십시오. Webhook을 추가하더라도 이벤트 수신과 최종 테스트 가능 상태 확인은 별도 조건으로 남겨야 합니다. Apple의 Webhook 이벤트 유형 정의도 이벤트 종류를 구분해 다룹니다.
현재 방식이 공유 큐, 고정 재시도, SSH 세션 의존에 묶여 있다면 앱 간 장애가 전파되고, 중복 업로드와 불필요한 API 호출이 늘어납니다. 반면 VpsMesh의 원격 Mac을 사용하면 상시 실행되는 호스트에서 업로드와 상태 관리 작업을 분리하고, SSH가 끊긴 뒤에도 저장된 작업 상태를 기준으로 복구하는 운영 방식을 구성할 수 있습니다. 특히 직접 Mac을 구매해 24시간 켜 두는 방식은 초기 하드웨어 비용, 유지 관리, 장애 대응을 모두 직접 부담해야 하므로 일시적인 TestFlight 자동화나 여러 환경의 검증에는 과할 수 있습니다. 장기적으로 고정 부하가 크거나 물리 장치 연결이 필요하다면 직접 Mac이 더 적합하지만, 임시 배포 환경이나 팀 단위 원격 실행이 목적이라면 Mac mini 원격 대여 방식을 비교해 보십시오.
이번 주에는 업로드 체인과 상태 조회 체인을 분리한 뒤, 실제 TestFlight 빌드 하나로 재시도 경계를 검증하십시오. 지속적인 원격 실행이 필요하다면 자격 증명 분리와 SSH 중단 복구까지 포함한 운영 기준을 함께 적용해야 합니다.