한정

Flutter iOS CI가 클라우드 Mac에서 느린 이유: 3계층 캐시 무효화가 핵심

CI Flutter · iOS 빌드
2026-06-04 약 15분

핵심: Flutter iOS CI는 캐시 무효화 전파 문제입니다. 1순위 CocoaPods, 2순위 DerivedData. §0·§0.1클라우드 Mac·GitHub Actions macOS runner에서 무엇을 먼저 고칠지 정해집니다.

30초 요약(우선순)

  1. 주병목: Pods(네트워크+해석) > DerivedData > Flutter cache — Pods 먼저.
  2. 단일 인과: 「세 층 모두 느림」이 아니라 상류 캐시 미스가 하류 재작업을 강제(§1).
  3. 유일 원칙(§0): 캐시는 lockfile/SDK에 묶고 날짜에 묶지 않음.
  4. 바쁠 때: §0+§0.1+§4 CocoaPods; hot 느리면 §5.
  5. §7: restore 순서가 key 문자열보다 중요.
클라우드 Mac에서 Flutter iOS CI 빌드 및 캐시 최적화
표지는示意. Benchmark 조건은 아래 표 참고.

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 ipaRunning pod install...에 멈추면 §0.1: Pods 캐시·네트워크 우선. 구간: pub getpod installxcodebuild.

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/Pods cache, key=hashFiles('ios/Podfile.lock').
  • 컴플라이언스: Pods/ 커밋, pod install --deployment.

무차별 pod update는 lock 표류·§0 파괴.

암시 pod면 cd ios && pod install --verboseflutter 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)
1pub 🔥PUB_CACHEpubspec.lock + Flutter SDK
2–3pods 🔥🔥🔥~/.cocoapods + ios/PodsPodfile.lock + CP version
4deriveddata 🔥🔥$DERIVED_DATA_PATHXcode + 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 임대 온보딩 · 사양·기간 · 구성