GitHub Actions iOS CI가 느릴 때 범인은 CPU가 아니라 캐시가 없거나 key가 어긋난 경우가 많습니다. 핵심은 세 군데입니다:
- CocoaPods:
Pods/미캐시 → 매 run마다pod install처음부터 - Swift Package Manager(SPM): 의존성 그래프를 매번 resolve,
.build가 job마다 지워짐 - Xcode DerivedData: 콜드 빌드로 Swift 전량 컴파일·모듈 재구축
CocoaPods / SPM / DerivedData 캐시를 제대로 연결하면 warm 빌드 벽시계가 보통 30%–60% 줄어듭니다(Shadow 대조는 워터폴 분해).
1. GitHub Actions iOS CI가 느리게 느껴지는 이유
먼저 나오는 말은 대개 이렇습니다. 「macos-latest가 약한 거 아냐?」「M4나 상위 머신으로 갈아타야 해?」
현장의 Xcode CI 지연에서는 병목이 CPU보다 눈에 잘 안 띄는 세 가지 비용에 몰리는 경우가 많습니다.
- 의존성 재 resolve(CocoaPods / SPM)
- DerivedData 콜드 스타트(전량 컴파일)
- actions/cache 미스 — 매번 첫 빌드처럼 느껴짐
GitHub Actions는 이를 증폭합니다. runner는 일회용(job마다 깨끗한 디스크), 캐시가 저절로 남지 않고, 다중 브랜치·다중 job에서 key가 쉽게 오염됩니다. 같은 commit인데 빠른 run과 느린 run을 추적 중이라면, 먼저 cold/warm을 나눈 뒤 이 글의 캐시 설계에 맞춰 보세요.
2. 벽시계가 사라지는 지점
2.1 CocoaPods(Pods 캐시)
전형적 콜드 비용: pod install 약 3–8분.
본질: 매 CI에서 pod install을 처음부터 — resolve, 다운로드, 통합을 전부 다시.
대응: actions/cache로 Pods/ 저장. key는 Podfile.lock 해시 + github.ref + arm64. CI에서는 pod install --deployment.
2.2 Swift Package Manager(SPM)
전형적 비용: 패키지 그래프 resolve에 1–5분.
본질: 매 run 재 resolve, .build 삭제, Package.resolved가 key에 없음.
대응: ~/Library/Caches/org.swift.swiftpm과 .build 캐시. key에 Package.resolved 해시(§3.2).
2.3 Xcode DerivedData
전형적 콜드 증분: 전량~준전량 컴파일 5–15분.
본질: DerivedData 미재사용 → Swift 전량 컴파일, 에셋 재처리, 모듈 재구축.
대응: xcodebuild -derivedDataPath DerivedData로 경로 고정 후 해당 경로 캐시(§3.3).
3. 동작하는 GitHub Actions iOS CI 캐시 설정
runs-on: macos-latest용. 아래 세 블록을 같은 job에 pod install / xcodebuild 앞에 넣으세요(저장은 actions/cache@v4가 자동 처리).
3.1 CocoaPods 캐시(필수)
- name: Restore Pods cache
uses: actions/cache@v4
with:
path: Pods
key: pods-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
pods-arm64-${{ github.ref }}-
- name: Pod install
run: pod install --deployment
3.2 SPM 캐시(필수)
- name: Restore SPM cache
uses: actions/cache@v4
with:
path: |
~/Library/Caches/org.swift.swiftpm
.build
key: spm-arm64-${{ github.ref }}-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
spm-arm64-${{ github.ref }}-
3.3 DerivedData 캐시(임팩트 큼)
- name: Restore DerivedData cache
uses: actions/cache@v4
with:
path: DerivedData
key: dd-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
dd-arm64-${{ github.ref }}-
- name: Build iOS
run: xcodebuild -scheme MyApp -derivedDataPath DerivedData ...
스텝 시간을 비교하기 전에 로그에서 Cache hit occurred를 확인하세요. 미스면 key와 path부터 — Xcode 버전 탓으로 돌리지 마세요.
4. cache key 설계(여기 건너뛰지 말기)
iOS CI 최적화가 runner가 아니라 key에서 무너지는 사례, 현장에서 흔합니다.
| ✔ 넣어야 할 것 | ❌ 흔한 실수 |
|---|---|
아키텍처(arm64) |
arm64/x86 미분리 — runner 바꾸면 전부 미스 |
브랜치(github.ref) |
락 파일만 — DerivedData가 브랜치 간 오염 |
| 락 파일 해시 | restore-keys가 너무 넓음(예: dd-만) → 잘못된 히트 |
락 파일이 바뀌면 dd-arm64-${{ github.ref }}- 같은 접두사로 일부 재사용 가능. 다중 scheme이면 key에 ${{ matrix.scheme }}. 같은 runner에서 병렬 job이 DerivedData 루트를 공유하지 않게 — ${{ github.run_id }} 하위 디렉터리로 격리.
5. self-hosted / Cloud Mac(고급)
Mac mini나 Cloud Mac self-hosted runner라면 actions/cache에만 묶이지 않습니다 — 디스크가 남아 warm이 더 안정적입니다.
- DerivedData 경로 고정
- Pods 디렉터리 고정
- 온디스크 warm 캐시(job당 30–90초 업로드/다운로드 생략)
export DERIVED_DATA=/Volumes/Data/DerivedData/App-${{ github.run_id }}
export PODS_ROOT=/Volumes/Data/Pods/${{ github.ref_name }}
job 전체 예시와 1TB 데이터 디스크 메모: 아래 workflow와 CI/CD 연동 FAQ.
env:
DERIVED_DATA: /Volumes/Data/DerivedData/App-${{ github.run_id }}
PODS_ROOT: /Volumes/Data/Pods/${{ github.ref_name }}
jobs:
build:
runs-on: [self-hosted, macos-m4-ios]
steps:
- uses: actions/checkout@v4
- name: Prepare dirs
run: mkdir -p "$DERIVED_DATA" "$PODS_ROOT"
- name: Pod install
run: pod install --deployment
- name: xcodebuild
run: xcodebuild -scheme MyApp -derivedDataPath "$DERIVED_DATA" build
6. 캐시 미스 ≠ 느린 runner(흔한 오독)
GitHub Actions iOS CI가 느리다고 runner나 Xcode 버전 탓을 하기 쉽습니다. 실제로는 아래가 더 많습니다.
| 보이는 현상 | 더 그럴듯한 원인 |
|---|---|
| 갑자기 빌드가 느려짐 | 캐시 미스 |
| P95가 흔들림 | cold 빌드가 warm 통계에 섞임 |
| 무작위로 느려짐 | 병렬 job의 DerivedData 경합 |
| 의존성이 처음부터 재빌드 | cache key 오설정 |
iOS CI가 느린 이유? GitHub Actions xcodebuild 속도 편차와 함께 보세요: cold/warm 분리 → 캐시 조정 → 그다음 하드웨어 평가(2026년, iOS CI/CD가 Mac mini M4에서 도는 이유).
7. 실측 개선 폭
캐시만 고쳤을 때 전형적 절약:
| 레이어 | 콜드 미스 시 추가 | 캐시 연결 후 |
|---|---|---|
| CocoaPods | 3–8분 | warm에서 보통 <30초–3분 |
| SPM | 1–5분 | 히트 시 resolve 대폭 단축 |
| DerivedData | 5–15분 | warm은 대부분 증분 컴파일 |
14일 Shadow에서 캐시 최적화만으로 warm 벽시계가 약 −1:40. 큐 제거와 self-hosted 고정 디스크를 겹치면 warm P95는 14:12 → 6:05(−57%) — GitHub Actions macOS 러너 최적화 참고.
8. FAQ
GitHub Actions iOS CI는 왜 느린가?
CPU보다 CocoaPods / SPM / DerivedData가 미캐시이거나 key가 어긋난 경우가 많습니다. 일회용 runner가 미스를 키웁니다.
GitHub Actions에서 CocoaPods를 캐시하려면?
Pods/를 캐시. key: pods-arm64-${{ github.ref }}-${{ hashFiles('**/Podfile.lock') }}. §3.1 참고.
iOS CI에서 SPM을 캐시하려면?
swiftpm 글로벌 디렉터리와 .build를 캐시. key는 Package.resolved에 바인딩. §3.2 참고.
DerivedData 캐시 path는 어떻게?
actions/cache의 path는 xcodebuild -derivedDataPath와 일치해야 합니다. 시스템 기본 경로는 필요 없습니다.
캐시가 너무 큼?
GitHub은 항목당 약 10GB 상한. DerivedData는 scheme별로 key를 나누거나 self-hosted 고정 디스크에 두고 업로드를 생략.
macos-latest vs self-hosted?
호스팅은 actions/cache만 가능, job 사이에 지워질 수 있음. self-hosted는 /Volumes/Data 고정으로 warm이 안정적. GitHub Actions macOS 러너 최적화에서 대조.
9. 정리와 관련 가이드
iOS CI 최적화에서 ROI가 큰 건 칩 교체 전에 CocoaPods·SPM·DerivedData 캐시를 연결하는 것 — workflow는 오늘부터 시험할 수 있습니다.
같은 시리즈:
- iOS CI가 느린 이유? GitHub Actions xcodebuild 속도 편차 — cold/warm, P95 통계
- GitHub Actions macOS 러너 최적화: P95 57% 단축 + iOS CI 실전 가이드 — P95 −57%, Shadow 워터폴
- 구매 vs 임대 · 월 500회 iOS 빌드: Mac mini 구매 vs 클라우드 Mac 렌탈 비용 분석
- 2026년, iOS CI/CD가 Mac mini M4에서 도는 이유 · CI/CD 연동 FAQ
DerivedData와 Pods 경로를 고정하고 싶다면?
Vuncloud Cloud Mac M4 Pro는 1TB 데이터 디스크 포함 — self-hosted iOS CI용. actions/cache 왕복을 줄이고 warm을 안정시키기 좋은 구성입니다.