혜택

xcodebuild가 CI에서 로컬보다 2~3배 느린 이유

CI Flutter iOS CI
2026-06-08 약 12분

Flutter iOS 팀이 바로 적용할 수 있도록 CI 느림 현상을 단계별로 측정하고, 셀프호스팅 Runner 전환까지 연결했습니다.

Flutter iOS CI 로컬 대비 CI 시간 격차
로컬에서는 정상인데 GitHub Actions에서는 매번 콜드 스타트가 발생해 xcodebuild 전후 단계까지 포함하면 2~3배 느려지기 쉽습니다.

먼저 알아야 할 핵심

  1. 문제는 xcodebuild 하나가 아니라 캐시 수명과 실행 환경 수명 불일치입니다.
  2. GitHub-hosted는 매 실행 초기화, 셀프호스팅 Runner는 워밍 상태 유지가 가능합니다.
  3. DerivedData와 CocoaPods 캐시를 지속화하면 대기 시간을 크게 줄일 수 있습니다.
  4. 디스크 압박과 서명 준비 오버헤드는 컴파일 지연으로 오해되기 쉽습니다.
  5. Flutter 팀은 Mac 임대로 48시간 계측 후 월 단위 셀프호스팅 Runner를 결정하는 방식이 안전합니다.

로컬 Mac vs GitHub Actions: 추가 시간은 어디서 생기나

로컬에서 벤치마크할 때 대부분 팀은 이런 전제로 비교합니다.

  • 로컬: 지속 SSD + 워밍 캐시 / CI: 일회성 VM + 콜드 의존성.
  • 로컬: 동일 사용자 서명 상태 / CI: 매 실행 keychain 부트스트랩.
  • 로컬: 같은 호스트 반복 빌드 / CI: 브랜치별 캐시 미스 증가.

반면 CI 파이프라인에서는 보통 다음과 같이 동작합니다.

  • 매 실행마다 깨끗한 워크스페이스에 checkout
  • flutter build ipa 또는 xcodebuild archiveRelease로 실행
  • 호스티드 Runner는 캐시가 잘 남지 않아 매번 콜드 스타트

팀이 `xcodebuild archive`만 비교하면 실제 원인을 놓칩니다. checkout, pod install, keychain 생성, DerivedData 무효화가 누적되어 체감 2~3배로 보입니다.

xcodebuild가 CI에서 느려지는 5가지 원인

GitHub Actions 환경과 셀프호스팅 Runner 전환 단계 모두에서 반복되는 패턴입니다.

  1. 1) 무상태 Runner가 의존성 레이어를 매번 리셋 GitHub-hosted VM은 job마다 초기화되어 SPM, Pods, 모듈 캐시가 안정적으로 워밍되지 않습니다. actions/cache만으로는 변동을 모두 흡수하기 어렵습니다.
  2. 2) DerivedData 경로 변동으로 증분 빌드 실패 임시 경로를 쓰면 로컬에서 재사용되던 결과물이 CI에서 무효화되어 재컴파일이 크게 늘어납니다.
  3. 3) Flutter 플러그인 변경이 Pods 해석을 콜드화 플러그인 업데이트는 Pod 해석 비용을 키웁니다. lockfile 관리와 deployment 모드가 없으면 `pod install` 시간이 불안정해집니다.
  4. 4) 서명/Keychain 단계의 직렬 오버헤드 인증서 import, keychain 생성/잠금해제는 CI에서 매번 수행됩니다. 로컬은 이미 준비된 상태라 차이가 커집니다.
  5. 5) 디스크 및 inode 압박이 빌드 쓰루풋 저하 CPU만 보면 오진하기 쉽습니다. 남은 SSD 용량과 inode 여유가 부족하면 인덱싱/쓰기 성능이 급격히 떨어집니다.

단계별 시간 로그 표 (Flutter iOS 예시)

플랫폼 교체 전에 먼저 이 형식으로 10회 이상 계측해 원인을 숫자로 확인하세요.

단계 로컬 Mac GitHub Actions 주요 원인
flutter pub get0m25s1m10spub 캐시 워밍 부족
pod install1m30s4m45sSpecs + plugin pod 콜드 해석
xcodebuild compile/archive6m40s12m20sDerivedData 재사용 실패
codesign/export0m55s2m00s매 실행 keychain 초기화
총합9m30s20m15s약 2.1배

xcodebuild 탓하기 전에

xcodebuild 전에 pod install을 놓치지 않았는지 확인하세요. CI에서 CocoaPods가 느려지는 대표 원인은 세 가지입니다.

  • Podfile.lock 미커밋 — 의존성 해석이 매번 불안정
  • ios/Pods 캐시 없음 — job 종료 후 삭제
  • 불필요한 pod repo update — lockfile이 있으면 보통 불필요

CI에서는 pod install --deployment을 쓰고 Pods를 동일 SSD 경로에 유지하세요. 셀프호스팅 Mac mini는 GitHub 캐시 업로드/다운로드 대기를 줄입니다.

DerivedData, Pods, 캐시의 실전 설계

Flutter iOS에서는 DerivedData 절대 경로 고정과 Pods 캐시 안정화가 가장 큰 개선을 만듭니다. 셀프호스팅 Runner를 쓰면 CI를 일회성 환경이 아닌 지속 최적화 가능한 시스템으로 바꿀 수 있습니다.

GitHub-hosted를 유지해도 경로/lockfile을 결정적으로 관리하면 개선됩니다. 전환 가능하다면 셀프호스팅 Runner가 로컬과 유사한 워밍 경로를 제공합니다.

  • target별 고정 derivedDataPath 사용.
  • `pod install --deployment`로 lockfile 드리프트 차단.
  • 디스크 여유와 inode를 CI 핵심 지표로 상시 관찰.

수정 패턴: 경로 고정 + 의존성 복원 결정성

아래 스니펫은 과한 트릭보다 운영 안정성을 우선한 베이스라인입니다.

  1. workflow에 concurrency 추가 — 같은 브랜치의 오래된 빌드 취소
  2. 테스트는 Linux에서 — Mac 시간을 flutter test에 쓰지 않기
  3. Pods + DerivedData 영속화 — 셀프호스팅은 고정 경로, 호스티드는 세분화된 cache key
  4. 배포 flavor만 빌드 — 6개 flavor 스캔은 피하기
  5. Xcode / Flutter 버전 고정 — CI 내부 upgrade 금지
# .github/workflows/ios-ci.yml
jobs:
  ios:
    runs-on: [self-hosted, macOS, ARM64, flutter-ios]
    steps:
      - uses: actions/checkout@v4
      - name: Restore caches
        run: |
          mkdir -p "$HOME/Library/Caches/CocoaPods"
          mkdir -p "$HOME/.pub-cache"
      - name: Build with stable DerivedData path
        run: |
          flutter pub get
          cd ios
          xcodebuild             -workspace Runner.xcworkspace             -scheme Runner             -configuration Release             -destination 'generic/platform=iOS'             -derivedDataPath "$HOME/ci-derived/runner"             CODE_SIGNING_ALLOWED=NO
      - name: Keep pod install deterministic
        run: |
          cd ios
          pod install --deployment

이 기본만으로도 30~50% 개선되는 경우가 많고, 이후 Runner 풀 전략으로 추가 최적화할 수 있습니다.

속도 회귀 머지 전 체크리스트

Flutter/Xcode 업데이트 뒤 CI가 느려졌다면 반드시 실행하세요.

  • ☐ CI는 Release/IPA, 로컬 비교는 Debug
  • ☐ job에서 flutter clean 또는 DerivedData 삭제
  • pod install 로그에 매번 대량 Installing
  • ☐ GitHub 호스티드 Runner 사용, 로컬 SSD 캐시 없음
  • ☐ 16GB 머신에서 여러 job/시뮬레이터 동시 실행
  • ☐ README만 바꿔도 iOS 전체 빌드 트리거
  • ☐ CI Xcode 버전이 로컬과 불일치

앞 3개에 체크되면 캐시부터, 뒤 2개면 트리거 조건과 머신 스펙을 우선 점검하세요.

FAQ

GitHub Actions는 항상 로컬보다 느린가요?

항상은 아니지만 Flutter iOS에서는 캐시 워밍이 안 되면 자주 느립니다. 셀프호스팅 Runner가 격차를 줄여줍니다.

바로 셀프호스팅 Runner로 옮겨야 하나요?

먼저 계측이 우선입니다. 콜드 스타트 손실이 반복되면 한 workflow를 옮겨 10회 중앙값으로 비교하세요.

고가 하드웨어가 꼭 필요합니까?

대부분 아닙니다. 안정적 캐시가 가능한 Mac 임대 환경이면 충분히 개선됩니다.

팀 설득은 어떻게 하나요?

단계별 시간과 대기 비용, 그리고 릴리스 예측 가능성 개선을 함께 제시하면 합의가 빨라집니다.

Flutter iOS CI를 재설계할 때 아래 순서를 권장합니다.

실제 Apple Silicon에서 안정적인 iOS CI 속도를 원한다면

kvmboot cloud Mac은 Flutter iOS 워크로드에 맞춘 전용 호스트를 제공합니다. Mac 임대(일 단위)로 계측 후 셀프호스팅 Runner 월 운영으로 확장하세요.

CI 변동성 디버깅보다 배포 속도 회복에 집중하려면, cloud Mac 요금제 보기