30초 요약(우선순)
- 주병목: Pods(네트워크+해석) > DerivedData > Flutter cache — Pods 먼저.
- 단일 인과: 「세 층 모두 느림」이 아니라 상류 캐시 미스가 하류 재작업을 강제(§1).
- 유일 원칙(§0): 캐시는 lockfile/SDK에 묶고 날짜에 묶지 않음.
- 바쁠 때: §0+§0.1+§4 CocoaPods; hot 느리면 §5.
- §7: restore 순서가 key 문자열보다 중요.
0. 캐시 설계의 유일한 원칙
CocoaPods·DerivedData·PUB_CACHE 전에 모든 key가 따르는 원칙:
CI 캐시는 «파일 저장»이 아니라 «빌드 결정성 묶음»이다.
pubspec.lock/Podfile.lock변경 → miss → 정상- Xcode/Flutter SDK 업그레이드 → miss → 정상
- Job 시각·날짜·랜덤 suffix만으로 miss → 안 됨
위반 증상: 매일 콜드 스타트 — «날짜»나 전체 삭제로 관리하고 lockfile+버전으로 관리하지 않음.
0.1 Flutter iOS CI 실제 병목 순서
이후 절은 동일 판단 — 세 층이 동등하지 않음:
Time ceiling (optimize in this order):
CocoaPods (network + resolve) 🔥🔥🔥 often ~70% of ceiling
↓
DerivedData (compile increment) 🔥🔥 hot builds → ~6 min tier
↓
Flutter cache (toolchain) 🔥 mainly first-run pub / engine
총판단: 느림은 세 문제 병렬이 아니라 Pods가 상한을 먼저 막음. Pods 미스 시 DerivedData만으로는 하류 공회전.
읽기 경로: 10분→§0+§0.1+§4; cold~15분인데 hot 느림→§5; §6는 이미지·첫 pub.
1. 단일 인과: 캐시 무효화가 IPA까지 전파
세 독립 시스템이 아니라 한 전파 연쇄. 단절이 상류인지 하류인지.
flutter build ipa가 Running pod install...에 멈추면 §0.1: Pods 캐시·네트워크 우선. 구간: pub get→pod install→xcodebuild.
1.1 전파 연쇄
인과 방향으로 읽기(§0.1과 일치):
[start · primary] CocoaPods miss / slow private specs
│ Pod tree rebuild, Specs fetch
▼
[propagate] DerivedData forced cold (full xcodebuild)
│
▼
[end] Slow IPA (often blamed on Flutter/Dart)
역방향도: pubspec.lock 변경→플러그인→pod install→DerivedData 무효. restore는 Pub 먼저, 투입 시간은 Pods→DerivedData→Flutter.
인용: «Flutter CI 느림»의 ~90%는 상류 연쇄 실패. DerivedData만 지워도 Pods 히트 후엔 콜드처럼 보임.
공식: Flutter iOS deployment · CocoaPods environment · Xcode Build Settings.
2. Benchmark: §0.1 가중치 검증
표는 Pods 선행·DerivedData 후행 투자 순서 증거. kvmboot 전형(중형 Flutter·Pod~40·Release·클라우드 Mac M4). 동일 commit 재측정.
| 단계 | 가중 | cold | hot | 설명 |
|---|---|---|---|---|
| 캐시 없음 | — | ~28 min | ~28 min | 연쇄 전단; hot≈cold |
| Pods+Specs만 | 🔥🔥🔥 | ~19 min | ~12 min | 최대 단발 단축(28→19); 최우선 |
| + DerivedData | 🔥🔥 | ~11 min | ~6 min | hot 핵심; Pods 히트 후 |
| + Flutter pub/엔진 | 🔥 | ~10 min | ~5 min | 주변; 첫 pub |
cold>20분이면 DerivedData 건드리지 말고 §4. cold~11분·hot>12분이면 §5 restore·DERIVED_DATA_PATH.
자가 측정: /usr/bin/time -l 또는 step summary 4행. 비교 시 commit·FLUTTER_VERSION 고정.
3. 듀얼 Agent 동일 호스트: 리소스 경계
2026-06-03 동반 글 클라우드 Mac 듀얼 AI Agent: Claude Code + Codex 분리(2026-06-03) 은 두 CLI가 한 worktree를 망가뜨리지 않게 하는 내용. 같은 Mac 임대 호스트에 야간 Flutter iOS CI를 겹칠 때:
- 캐시 루트 분리: CI 전용
DERIVED_DATA_PATH, Agent DerivedData와 공유 금지. - 스케줄 분리:
xcodebuild와 Agent 인덱싱 병렬은 swap 포화 — 16GB는 낮 Agent·밤 CI. - 교차 조작 금지: Agent가 CI
ios/Pods에서pod update금지.
4. CocoaPods CI 최적화 🔥🔥🔥
주층. Flutter iOS build slow의 ~70%는 pod install. 28→19분 — 여기서 안 되면 DerivedData 구제 불가. §0: Podfile.lock+CP.
4.1 환경 변수·영구 경로
export CP_HOME_DIR="${CP_HOME_DIR:-$HOME/.cocoapods}"
export COCOAPODS_PARALLEL_CODE_DOWNLOAD=true
# persist: ~/.cocoapods/repos + ~/Library/Caches/CocoaPods
사설 스펙/바이너리 Pod: GitHub Actions macOS runner 또는 클라우드 Mac을 Git과 동일 리전. .netrc/secret. Bitrise vs 셀프호스트 Mac Runner.
4.2 Pods/ 트리: cache vs 커밋
- 일반:
ios/Podscache, key=hashFiles('ios/Podfile.lock'). - 컴플라이언스:
Pods/커밋,pod install --deployment.
무차별 pod update는 lock 표류·§0 파괴.
암시 pod면 cd ios && pod install --verbose 후 flutter build ipa. 네트워크 vs 해석 분리.
5. Xcode DerivedData cache 🔥🔥
전파 구간. Pods 히트 후 hot ~6분. §0: Xcode+Flutter+lockfiles. §4 느리면 우선 아님.
export DERIVED_DATA_PATH="/var/ci/derived/flutter-${FLUTTER_VERSION}-xcode-${XCODE_VERSION}"
xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner \
-configuration Release -derivedDataPath "$DERIVED_DATA_PATH" \
-destination 'generic/platform=iOS' build
Tier 정리 — 매 Job 전체 삭제 금지. 디스크/inode: 클라우드 Mac Runner 디스크·inode.
6. Flutter 툴체인 cache 🔥
주변. PUB_CACHE·SDK cache는 첫 DL. 1–2분. lock 변경은 §1 상류 pod.
Job 간 build/ios 우선 restore 금지. iOS는 베어 macOS/클라우드 Mac( Docker 역할 분담.
7. GitHub Actions macOS runner Pipeline
§4–§6 YAML. restore(Pub→Pods→DerivedData); key 노력은 Pods→DerivedData.
| 순 | 블록 | 경로 | key(§0) |
|---|---|---|---|
| 1 | pub 🔥 | PUB_CACHE | pubspec.lock + Flutter SDK |
| 2–3 | pods 🔥🔥🔥 | ~/.cocoapods + ios/Pods | Podfile.lock + CP version |
| 4 | deriveddata 🔥🔥 | $DERIVED_DATA_PATH | Xcode + Flutter + lockfiles |
# .github/workflows/flutter-ios.yml (structure; macos-latest or self-hosted cloud Mac)
jobs:
build-ios:
runs-on: macos-14 # or runs-on: [self-hosted, cloud-mac]
steps:
- uses: actions/checkout@v4
# ── Restore (order: Pub → Pods Specs → Pods tree → DerivedData) ──
- name: Restore Pub Cache
uses: actions/cache/restore@v4
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Restore CocoaPods Specs
uses: actions/cache/restore@v4
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
key: pods-specs-${{ env.COCOAPODS_VERSION }}
- name: Restore Pods Tree
uses: actions/cache/restore@v4
with:
path: ios/Pods
key: pods-tree-${{ hashFiles('ios/Podfile.lock') }}
- name: Restore DerivedData
uses: actions/cache/restore@v4
with:
path: ${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
# ── Build ──
- name: Flutter Pub Get
run: flutter pub get
- name: Pod Install
run: cd ios && pod install --verbose
- name: Flutter Build IPA
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
# ── Save (mirror restore; save costly layers even on failure) ──
- name: Save Pub Cache
uses: actions/cache/save@v4
if: always()
with:
path: ${{ env.PUB_CACHE }}
key: pub-${{ hashFiles('pubspec.lock') }}-flutter-${{ env.FLUTTER_VERSION }}
- name: Save Pods + DerivedData
uses: actions/cache/save@v4
if: always()
with:
path: |
~/.cocoapods
~/Library/Caches/CocoaPods
ios/Pods
${{ env.DERIVED_DATA_PATH }}
key: dd-${{ env.XCODE_VERSION }}-${{ env.FLUTTER_VERSION }}-${{ hashFiles('pubspec.lock', 'ios/Podfile.lock') }}
빌드 후 서명: 클라우드 Mac codesign / Notarization — 생략.
함정: restore 전 pod install; 실패 시 save 생략. if: always().
8. 디스크·inode
세 층 cache로 256GB가 며칠 만에 60% — inode가 용량보다 먼저. 80%/85% 알림. 발판 스프린트 임시 빌드.
du -sh ~/Library/Developer/Xcode/DerivedData ~/.cocoapods "${PUB_CACHE:-$HOME/.pub-cache}" 2>/dev/null
df -h .; df -i .
9. 수용·트리아지(§0.1 순)
①🔥🔥🔥 lock 불변 시 pod<90s, cold~19분. ②🔥🔥 hot P95 ≥40% 단축, ~6분. ③🔥 첫 pub 생략. ① 건너뛰지 말 것.
| 증상 | 원인 | 조치 |
|---|---|---|
| hot≈cold 20분+ | 매 Job DerivedData 삭제 | -derivedDataPath·restore |
| pod만 느림 | CDN/사설 인증 | CP_HOME_DIR, .netrc, 리전 |
| 링크 오류 | Xcode 버전 간 DerivedData | key에 Xcode, 구 dir 폐기 |
| SSH 버벅 | 디스크/inode | df + 디스크治理 |
10. FAQ
10.1 클라우드 Mac/GitHub Actions에서 특히 느린 이유?
CocoaPods 캐시 미스가 전파로 DerivedData 냉각. §0.1: Pods→DerivedData.
10.2 CocoaPods vs DerivedData?
CocoaPods 먼저. 28→19; DerivedData는 Pods 후 hot.
10.3 DerivedData만으로 충분?
아니오. §0.1·§2🔥🔥🔥.
10.4 macOS runner Flutter cache
§7: Pub→Pods→DerivedData; if: always(); key=lock/SDK.
10.5 듀얼 Agent 동기
2026-06-03 글은 worktree; 본문은 CI 캐시 루트. 별도 DERIVED_DATA_PATH.
11. 결론
Flutter iOS CI는 캐시 무효화 전파 연쇄. CocoaPods(🔥🔥🔥)→DerivedData(🔥🔥)→Flutter(🔥). §4→§5→§6–7. Pods가 상한, DerivedData가 hot 안정.
48시간 iOS CI cache strategy 검증
이미 클라우드 Mac 듀얼 AI Agent: Claude Code + Codex 분리(2026-06-03) 통과한 같은 클라우드 Mac에 야간 Pipeline. 1일 cold, 2일 §7 restore, 3일 §2 hot ~6분.
SSH·M4: Mac 임대 온보딩 · 사양·기간 · 구성