GitHub Actions의 workflow_job 이벤트는 작업을 queued, in_progress, completed 상태로 구분합니다. 공식 이벤트 문서 기준으로 이 세 상태를 기록하면, 단일 Runner가 평소에는 한가해도 발행 시 계속 밀리는 이유를 확인할 수 있습니다. GitHub Actions iOS 동시 빌드에 필요한 Mac 수는 개발자 수가 아니라 피크 동시 Job, 각 Job의 Runner 점유 시간, 허용 가능한 대기 시간, 발행 환경 분리 여부로 정해야 합니다. 이번 주에는 먼저 단일 Mac의 실제 대기 구간을 수집하고, 겹침이 반복될 때만 두 번째 Mac 또는 발행 기간 추가 임대를 검토하는 것이 맞습니다.

이 글은 이미 GitHub Actions self-hosted runner로 iOS 빌드, 테스트, TestFlight 발행을 실행하는 독립 개발자를 위한 글입니다. 한 대의 원격 Mac이 충분한지 판단하려는 경우와, 풀 리퀘스트 테스트와 발행 작업을 동시에 처리하려는 소규모 팀에 특히 적합합니다. 발행 파동이 특정 기간에만 생긴다면 장기 구매보다 주간 또는 월간 운영이 합리적인지도 함께 확인합니다.

01

개발자 수가 아니라 Job 점유 시간으로 용량을 잡습니다

Workflow는 전체 자동화 흐름입니다. Job은 실제 Runner를 점유하는 작업 단위입니다. Runner는 Job을 실행하는 Mac 호스트입니다. Xcode 내부 병렬 빌드는 한 Job 안에서 일어날 수 있으므로 Mac을 추가한다고 단일 Job 시간이 자동으로 줄어들지는 않습니다.

먼저 다음 항목을 분리해 기록합니다.

  • 일반 커밋 검증
  • 풀 리퀘스트 테스트
  • 시뮬레이터 테스트
  • Archive 생성
  • 서명과 업로드
  • App Store Connect의 서버 처리

마지막 항목은 Runner가 계속 점유하는 시간이 아닐 수 있습니다. 따라서 업로드 뒤의 서버 처리 대기까지 Mac 용량에 합치면 실제보다 많은 Runner가 필요하다고 오판하게 됩니다. App Store Connect의 빌드 업로드 안내에서 업로드와 이후 처리 단계를 구분해 기록해야 합니다.

용량을 계산할 때는 하루 총 빌드 수보다 같은 시간대에 겹친 Job을 봅니다. 평일 대부분의 시간에 단일 Runner가 비어 있어도, 풀 리퀘스트 검증과 TestFlight Archive가 같은 시점에 시작되면 두 번째 실행 환경이 필요할 수 있습니다.

GitHub의 Actions 성능 지표 안내는 실행 시간과 대기 시간 같은 관찰 지점을 제공합니다. 개인 저장소에서 조직 단위 지표를 충분히 볼 수 없다면, 실행 기록과 Job 로그를 내보내고 시작 시각, queued 진입 시각, 실행 시작 시각, 종료 시각을 별도로 적습니다.

주의: 실패 후 재실행을 새 성공 작업으로만 세면 용량 부족이 가려집니다. 최초 실패 원인, 재실행 횟수, Runner가 실제로 점유된 시간을 함께 남겨야 합니다.

02

단일 Mac이 맞는 독립 개발자의 조건

독립 개발자가 앱 하나를 관리하고, 풀 리퀘스트 수가 많지 않으며, 정식 발행을 업무 시간 밖으로 옮길 수 있다면 단일 Mac부터 시작합니다. 이때 중요한 것은 Runner의 평균 이용률이 아니라 긴급 발행이 일반 테스트 뒤에서 얼마나 오래 기다리는지입니다.

일반 검증에는 낮은 우선순위를 부여하고, 정식 Archive와 발행에는 별도 Workflow를 둡니다. 오래된 커밋 검증이 새 커밋의 검증을 계속 쌓이게 한다면 GitHub Actions 동시 실행 제어를 사용해 이전 작업을 취소할 수 있는지 확인합니다.

단일 Mac을 유지하기 전에 다음을 확인합니다.

  • [ ] 최근 Workflow 기록에서 Job별 대기 시작과 실행 시작 시각을 분리했습니다.
  • [ ] 일반 빌드, 테스트, Archive, 업로드를 서로 다른 Job으로 분류했습니다.
  • [ ] 반복 다운로드와 의존성 설치가 매번 실행되지 않는지 확인했습니다.
  • [ ] 사용하지 않는 테스트 대상과 중복 시뮬레이터 조합을 제거했습니다.
  • [ ] 발행 Job이 일반 검증 Job에 밀릴 때의 우선순위를 정했습니다.
  • [ ] Mac이 재시작되거나 연결이 끊긴 뒤 Runner가 다시 작업을 받을 수 있는지 확인했습니다.

이 조건을 통과하고 발행 지연이 드물다면, 독립 개발자에게 한 대의 원격 Mac은 충분할 수 있습니다. Xcode 27 프로젝트를 사용하더라도 먼저 도구 버전 자체보다 현재 Workflow의 겹침과 불필요한 Job을 확인해야 합니다. Xcode의 빌드 타이밍 정보를 이용해 실제 컴파일 구간을 확인하는 방법은 Apple의 빌드 속도 문서에 설명되어 있습니다.

장기적으로 한 대를 유지할지 고민한다면 Mac mini 대여 비용과 운영 조건을 작업량이 낮은 기간의 고정 비용과 비교해 보십시오. 하드웨어 가격만 보지 말고 Runner 복구, 발행 키 관리, 교체 지연까지 같은 표에 기록해야 합니다.

03

작업이 겹치는 고빈도 개발자는 두 번째 환경을 검토합니다

기능 브랜치를 자주 만들고 자동 테스트와 TestFlight 빌드를 반복한다면 단일 Mac의 대기열이 짧은 파동으로 반복될 수 있습니다. 이 경우 두 번째 Mac의 핵심 가치는 단일 Job의 속도 향상이 아닙니다. 테스트와 발행을 동시에 실행하고, 한 호스트가 멈췄을 때 다른 호스트로 회복하는 데 있습니다.

먼저 Workflow를 나눕니다.

  • 빠른 코드 검증은 일반 Runner로 보냅니다.
  • 시뮬레이터 테스트는 테스트용 태그를 사용합니다.
  • Archive와 서명 업로드는 발행용 태그로 제한합니다.
  • 이전 커밋의 테스트가 의미 없으면 동시 실행 취소를 적용합니다.

Runner 선택은 라벨에 의존합니다. Runner 선택 규칙라벨 적용 방법을 확인해 일반 Job이 발행 환경으로 잘못 들어가지 않게 합니다.

다음과 같은 경우에는 두 대가 더 설득력 있습니다.

  • 테스트가 끝나기 전에 Archive가 반복해서 시작됩니다.
  • 발행 시간대마다 일반 풀 리퀘스트 검증이 누적됩니다.
  • 한 호스트의 유지보수 중 발행을 멈추면 안 됩니다.
  • 서명 키와 배포 프로파일을 일반 테스트 환경과 분리해야 합니다.

반대로 단순히 한 Job의 컴파일 시간이 길다는 이유만으로 Mac을 추가하지는 않습니다. 캐시, 의존성 설치, 테스트 범위, Xcode 빌드 타이밍을 먼저 손봐야 합니다. 테스트 결과의 실패 위치와 실행 정보를 확인하려면 Apple의 테스트 결과 문서를 기준으로 로그를 정리합니다.

04

소규모 팀과 여러 앱은 공용 풀과 발행 풀을 분리합니다

여러 개발자가 동시에 풀 리퀘스트를 열면 목표는 Runner를 한 번도 기다리지 않게 만드는 것이 아닙니다. 허용할 수 있는 코드 피드백 대기 시간을 정하고, 그 시간을 넘는 피크가 반복되는지 확인하는 것입니다.

소규모 팀은 빠른 검사, 시뮬레이터 테스트, 발행 Job을 같은 라벨에 넣지 않는 편이 안전합니다. 여러 앱을 관리한다면 저장소 이름만으로 라우팅하지 말고 작업 종류와 보안 수준을 함께 사용합니다. 발행용 Runner에는 서명 자격 증명, Bundle ID, Team ID, 인증서 경로가 연결되므로 로그와 설정에서 실제 값을 노출하지 않아야 합니다.

발행 전용 환경을 분리해야 하는 이유는 속도보다 권한입니다. 일반 테스트 Job이 발행 키를 읽을 수 있으면 Runner 수를 늘려도 운영 위험은 줄지 않습니다. 반대로 발행용 Mac이 한 대뿐이라면 유지보수 중 App Store 업로드가 멈추므로, 복구용 환경 또는 발행 기간 임시 확장을 준비해야 합니다.

GitHub Actions에서 self-hosted runner 라우팅을 확인하는 문서를 기준으로 라벨과 그룹을 설계합니다. 저장소, 계정, 라벨, 경로와 로그는 외부 공유 전에 반드시 탈식별화합니다.

05

단일, 이중, 발행 기간 확장을 나누는 판단표

아래 표에서 중요한 열은 팀 규모가 아니라 작업 겹침과 발행 중단 허용 여부입니다.

운영 방식 적합한 부하 장점 중단 조건
Mac 한 대 단일 앱, 낮은 빈도, 발행 시간 조정 가능 관리 대상과 자격 증명 범위가 작음 발행이 일반 테스트에 반복해서 밀림
Mac 두 대 테스트와 Archive가 자주 겹침, 유지보수 중에도 발행 필요 병렬 처리와 장애 회복을 함께 확보 두 번째 호스트가 대부분 유휴 상태이고 대기 문제가 Workflow 정리로 해결됨
발행 기간 추가 임대 특정 발행 창에만 피크 발생, 평소 부하는 낮음 장기 유휴 하드웨어 없이 피크만 흡수 추가 Mac 준비와 도구 설치가 발행 전에 끝나지 않음
개발 풀과 발행 풀 분리 여러 앱, 여러 저장소, 서명 권한 분리 필요 일반 작업과 민감한 작업의 경로를 분리 라벨과 권한 검증이 없거나 복구 절차가 없음

일반 작업은 공유 풀에 두고 발행 작업만 별도 환경에 두는 구성이 가장 흔한 타협점입니다. 다만 새 Mac을 추가하기 전에 작업 라우팅이 실제로 분리되었는지, 새 환경에서 같은 Xcode와 의존성 버전을 재현할 수 있는지 검증해야 합니다.

06

큐 데이터를 단일 Mac 수로 바꾸는 기록 절차

첫째, 최근 Workflow 기록에서 모든 Job의 상태 변화를 모읍니다. queued에서 in_progress로 넘어가는 구간이 실제 Runner 대기 시간입니다. 실행 시간은 in_progress에서 completed까지 따로 봅니다.

둘째, Job을 Build, Test, Archive, 서명 업로드로 분류합니다. 업로드 뒤 App Store Connect 처리 시간은 별도 서비스 시간으로 기록합니다.

셋째, 시간대별 동시 실행 수를 표시합니다. 하루 평균이 아니라 가장 겹치는 구간을 찾아야 합니다. 한 번의 긴 작업 때문에 생긴 일시적 지연인지, 매 발행마다 반복되는 패턴인지도 구분합니다.

넷째, 실패 재실행과 호스트 장애를 포함합니다. 단일 Mac 계획은 재부팅 뒤 Runner 복귀와 작업 재수신을 확인해야 합니다. 두 대 계획은 각 Job이 의도한 라벨로만 가는지, 발행 자격 증명이 다른 호스트에 복제되지 않았는지 확인합니다.

다섯째, 선택지를 한 번에 하나씩 바꿉니다. 먼저 중복 테스트와 이전 작업 취소를 정리합니다. 그래도 피크 대기가 반복되면 두 번째 상시 Mac을 검토합니다. 피크가 발행 기간에만 나타나면 추가 Mac의 주간 또는 월간 임대가 더 적합할 수 있습니다.

여섯째, 변화 후 다시 같은 지표를 비교합니다. 대기열이 줄지 않고 실행 시간만 늘었다면 Runner 수가 아니라 프로젝트나 도구 체인의 병목일 가능성이 큽니다.

07

용량 결정 카드를 이렇게 작성합니다

기록 항목 확인할 값 다음 결정
피크 동시 Job 같은 시간에 겹친 Build, Test, Archive 반복적으로 Runner 수를 넘으면 추가 환경 검토
대기 구간 queued에서 in_progress까지 허용 범위를 넘는 작업 종류를 우선 분리
실행 구간 컴파일, 테스트, Archive별 점유 시간 긴 작업의 캐시와 테스트 범위 점검
실패 재실행 실패 후 다시 실행된 Job 용량 부족과 코드 실패를 분리
서명 권한 발행 환경에 접근 가능한 Runner 발행 전용 라벨과 자격 증명 격리
장애 회복 재시작과 연결 단절 뒤 복귀 여부 단일 유지, 두 번째 Mac, 임시 확장 중 선택

개인 프로젝트에서 피크가 낮고 발행을 미룰 수 있다면 단일 Mac을 유지합니다. 테스트와 발행이 계속 겹치면 두 대로 분리합니다. 발행 기간에만 파동이 생기고 평소에는 유휴 시간이 길다면 해당 기간에만 원격 Mac을 추가하는 쪽이 맞습니다. 여러 앱과 여러 저장소가 같은 발행 창을 공유한다면 개발 풀과 발행 풀을 분리하고, 실제 큐 기록을 기준으로 주기적으로 조정합니다.

현재 단일 Mac을 계속 사용하는 방식은 평소 비용을 낮출 수 있지만, 발행 때 테스트가 밀리고 한 호스트의 장애가 전체 배포를 멈추며 서명 권한까지 한곳에 모이는 단점이 있습니다. 반대로 상시 두 대를 구매하면 피크가 없는 기간에도 하드웨어와 관리 비용이 남습니다. 용량 결정 카드에서 부족한 기간이 테스트 고점이나 발행 창으로 한정된다면, MESHLAUNCH의 원격 Mac 대여 기간과 구성을 먼저 확인하고 필요한 기간에만 환경을 추가하는 편이 현실적입니다. 새 Mac에서 라벨, Xcode 버전, 서명 격리, 재시작 복구를 검증한 뒤 장기 구매 여부를 결정하면 유휴 장비를 떠안지 않고도 병렬성과 회복성을 확보할 수 있습니다.