VPSSpark 블로그
← 개발 일기로

GitHub Actions가 iOS 개발을 느리게 만든다 — 2026년에 통한 Xcode CI 해법

서버실 노트 · Cloud Mac CI #9 · 2026.06.12 · 약 12분
현장 기록: 47분 대기열, 파이프라인 분리, Cloud Mac 자체 호스팅, Agent 가드레일

검색: GitHub Actions iOS 느림 · Xcode CI 대기 · macOS runner 자체 호스팅 · Cloud Mac

MacBook 코드 편집기와 터미널 — GitHub Actions Xcode CI를 살피는 iOS 팀
목요일 오후 Slack — “아직 Queued” — 익숙한 iOS 팀 풍경.

2026년 5월 29일, 목요일 15:03. 12명 규모 iOS 팀——Slack #ios-ci에 Run 링크가 올라왔다. 「로컬 Xcode에서는 12분 만에 통과했는데, PR은 아직 Queued」라는 한 줄과 함께. 30분 동안 답이 없었다. 이런 메시지는 일상이다. 48분 뒤 같은 개발자가 덧붙였다. 「Queued 47분, run 12분. 오늘 merge는 포기하자.」 회의실에서 나온 Release 담당이 휴대폰을 보며 말했다. 「ios-release Archive가 아직 대기열에 있어. PR 라인 또 막혔어.」

Slack #ios-ci 채널 실록: 개발자가 Queued 47분 보고, Release 담당이 Archive로 PR 라인 막힘 지적
그림 5 · Slack #ios-ci 실록 발췌(저장소·조직명 마스킹, 시각·수치는 원본 그대로)

아래 스크린샷은 장애 대응 지원 시 고객이 제공한 자료(마스킹 처리). 수치는 가공하지 않았습니다. GitHub 공식 문서의 Job 실행 시간과 queue_wait_seconds를 참고하면 동일 통계를 직접 재현할 수 있습니다.

이 팀은 앱 2개를 모노레포로 운영하고, CI는 전부 GitHub 호스트 macOS Runner를 탄다. 2025년까지는 참을 만했다. 2026년 봄이 되면 피드백 루프가 인내 한계를 넘었다. GitHub Actions 자체가 「고장 난」 것은 아니다——iOS 팀에게는 피크마다 막히는 공유 셔틀처럼 느껴지기 시작했다.

2832s
queue_wait_seconds
724s
run duration
3.9×
queued / run 비율

그 목요일: Job 페이지를 열면 노란 막대가 초록의 4배

Run #18472910356 Job 페이지 스크린샷이 넘어왔다——구두 보고가 아니라 타임라인이 붙은 실록이다. 노란 막대(Queued) 47분 12초, 초록(Run) 12분 4초. 화면 하단에 queue_wait_seconds: 2832, run duration: 724. 식은 단순하고 날카롭다. wait time >> run time.

GitHub Actions Job 타임라인: Queued 47m12s, Run 12m4s, run 18472910356
그림 1 · Run #18472910356 · ios-pr.yml · macos-latest · 2026-05-29 15:04 CST

당시 팀은 -jobs 병렬화와 DerivedData 캐시 키 변경을 회의에서 논의 중이었다——그런데 최근 macOS job 10건의 queued/run 열을 아무도 뽑아 보지 않았다. Release 라인은 더 답답했다. ios-release.ymlxcodebuild archive, 공증, TestFlight 업로드를 PR 단위 테스트와 같은 라인·같은 macos-latest 라벨에 몰아 넣었다. Archive 한 번에 25~40분, 희소한 동시 실행 슬롯을 점유하면서 PR 빨간불을 빨리 돌려줄 job까지 대기열로 끌고 들어갔다.

Queued만 먼저 줄이고 싶다면 사이트 macOS runner 대기열 진단 Runbook부터 읽어도 된다. 이들의 전환점은 「문제는 공급 쪽」이라 인정하고, 체감이 아니라 숫자로 회의실을 설득한 뒤에 시작됐다.

증거: failed job 10건 수기 표

5월 30일 스탠드업 전, 누군가 Actions 실행 목록에서 failed macOS job 10건을 손으로 옮겨 적었다(아래 그림). BI 도구 없음——표 한 장, 핵심 두 열 queued_srun_s. queued 중앙값 2650초, run 724초. 비율은 최대 4.5×. 회의실이 몇 초 조용해졌다. 「Xcode 17.4 탓 아닌가?」는 더 이상 나오지 않았다.

failed macOS job 10건 queued_s와 run_s 수기 통계 표
그림 3 · 2026-05-22 — 05-29 failed macOS job 10건 수기 표(비식별)
흔한 오진그림 3 신호더 그럴듯한 원인
GitHub 장애10/10 queued_s > run_smacOS 동시 실행 cap, 무거운 job이 슬롯 점유
프로젝트 비대화run_s 690–811s로 안정컴파일보다 대기가 주원인
무작위 push동일 PR 다중 run(그림 2 참고)Agent / bot 루프
인증서 만료서명 실패가 run마다 분산키체인 cold start, 병렬 경합

그림 2: 같은 PR이 6번 트리거됐다

대기열 문제는 전반전에 불과하다. 3월 팀은 코딩 Agent를 도입했다. CI 빨강 → 로그 읽기 → 자동 commit → Actions 재트리거. PR #847 Checks 페이지 스크린샷이 분명히 보여 준다——workflow run 6회. actor가 bot인 건 4회, 사람 2회. 겉으로는 CI지만 행동은 retry loop다. 자세히는 CI가 루프화된 뒤 일어나는 일을 참고.

Pull Request #847의 GitHub Actions workflow 실행 6건
그림 2 · PR #847 · ios-pr.yml 6회 트리거(4× bot · 2× 사람) · 2026-05-29

나중에 누군가 농담처럼 말했다. 「CI가 죽은 게 아니라, 우리가 영구기관으로 만들어 버린 거야.」 문제는 자동화 자체가 아니라 bot 수정마다 공용 대기열을 갉아 먹는 것, 전용 Runner 풀이 없다는 점이다.

전환점: Cloud Mac 1대와 5월 31일 파이프라인 분리의 밤

수기 표 이후 팀은 로그를 job 유형별로 쪼갰다. PR 검증·통합 빌드 약 70%, Simulator 단위 테스트 20%, Archive·업로드 10%——다른 팀의 하루 500회 iOS CI 실측 구성과 놀랍도록 비슷했다. Mac mini 8대 구매안, Xcode Cloud 이전안도 나왔다. 최종적으로 두 가지에 닿았다.

5월 31일, Apple Silicon Cloud Mac 1대를 임대해 self-hosted Runner로 등록(라벨 self-hosted, macOS, cloud-mac). 대조 실험은 단순하다. 동일 commit을 호스트 macos-latest와 Cloud Mac에서 각 50회 build——queued 분산이 「40분인 날도 5분인 날도」에서 「거의 몇 초」로 수렴했다.

그날 밤 파이프라인 분리. Archive, export, 업로드를 ios-pr.yml에서 ios-release.yml로 옮기고, PR 라인에는 build와 단위 테스트만 남겼다. 워밍 cron도 추가: 매일 새벽 Cloud Mac에서 xcodebuild build 1회, DerivedData는 디스크에 유지. 매번 actions/cache에서 cold pull하는 것보다 안정적이었다.

그림 6 · 동일 팀 wall time 중앙값(PR build, 2주간 각 50회)

호스트 macOS · queued
38 min
호스트 macOS · run
12 min
Cloud Mac self-hosted · queued
41 s
Cloud Mac self-hosted · run
9 min

데이터 출처: 고객 대조 실험 로그(2026-05-31 — 06-08). 그림 1·그림 4 단일 run과 교차 검증 가능.

6월 9일: 그림 4 Job 페이지가 드디어 「정상」처럼 보였다

파이프라인 분리 2주 뒤 월요일 아침, Slack에 Run #18510488201 스크린샷이 올라왔다. queued 41초, run 9분 17초, runner는 self-hosted, cloud-mac. 반응은 많지 않았다——iOS 팀에게 그게 최고의 칭찬이다.

GitHub Actions self-hosted Runner: Queued 41s, Run 9m17s, cloud-mac
그림 4 · Run #18510488201 · self-hosted, cloud-mac · 2026-06-09 09:11 CST

Agent에 울타리: Wiki에 적은 4조

파이프라인 분리로 Archive가 PR을 막는 문제는 해소됐다. bot 루프는 별도 대응. 팀은 bot 전용 브랜치와 낮은 우선순위 Runner 풀을 두고, 사람이 review한 뒤 merge하기로 했다. 4조의 하드룰을 Wiki에 박았다.

  • PR workflow에서 Archive / 스토어 업로드 금지
  • macOS job에 concurrency 필수, 동일 브랜치 중복 실행 방지
  • self-hosted Runner는 라벨 필수, 실험 workflow와 풀 혼용 금지
  • Agent 자동 push는 독립 브랜치만, 보호 브랜치 직접 push 금지
30분 자가 점검
최근 macOS job 10개를 열어 queue_wait_seconds와 run duration을 옮겨 적는다(그림 3 참고). 단일 PR workflow 횟수를 센다(그림 2 참고). Archive가 PR workflow에 남아 있는지 확인한다. queued 중앙값 > run이면 컴파일 파라미터보다 Runner 공급을 먼저 논의한다.

새 해법: GitHub를 버리는 게 아니라 공급 방식을 바꾸는 것

이 팀의 경험은 2026년 iOS 팀이 자주 닿는 네 가지로 압축된다. Cloud Mac self-hosted Runner 풀, PR / Release 분리, 워밍·디스크 캐시, Agent 가드레일. Xcode Cloud로 대체할 수 있나? Apple 생태계에 닫힌 구성이면 맞다. GitHub PR 체크와 Android 혼합 라인을 묶는다면 대부분 Actions는 유지하고 Archive만 전용 macOS 연산으로 옮긴다.

팀 규모·리듬흔한 랜딩
1–3명, 주간 릴리스호스트 macOS + 강 캐시; Release는 수동 Archive
5–15명, 일 mergeCloud Mac self-hosted 1–2대 + PR/Release 분리
15+명, 다중 AppApp별 라벨 Runner 풀 + 워밍 cron
Agent 수정 많음독립 bot 풀 + PR workflow Archive 금지

Apple 공식 xcodebuild 문서는 scheme과 destination 일치를 강조한다. CI 환경이 매번 cold start면 「로컬에선 통과」는 의미를 잃는다. 고객 말을 빌리면, 매일 47분 노란 막대를 보는 것보다 인증서 로테이션과 Xcode 메이저 업그레이드가 낫다는 쪽이다.

첫 대: Cloud Mac self-hosted Runner부터

Job 페이지가 그림 1처럼 노란 막대가 초록을 누른다면, 다음으로 값지는 투자는 보통 전용 Apple Silicon Cloud Mac 1대다. GitHub self-hosted Runner로 등록해 PR 빌드를 「대기열 복권」에서 그림 4처럼 예측 가능한 9분으로.

2주 대조 실험부터: 동일 브랜치를 호스트 macOS와 Cloud Mac self-hosted에서 각 50회 build하고, 그림 3 같은 표를 한 장 작성한다. 데이터가 알려 준다——느린 건 Xcode인지, 공급인지.

예측 가능한 Cloud Mac 1대로 시작해 Archive를 Release 라인으로 되돌린다. VPSSpark Cloud Mac 요금제 보기, 첫 iOS self-hosted Runner를 등록한다.

한정 혜택

iOS CI가 계속 Queued? Cloud Mac 자체 호스팅 Runner

GitHub Actions · 전용 Apple Silicon · PR/Release 분리

홈으로
한정 혜택 요금제 보기