이 글은 한 가지만 다룹니다: Flutter iOS CI/CD(핵심 키워드: Flutter iOS build on Mac mini)——Mac mini M4에서 flutter build ipa를 안정적으로 돌리고, CocoaPods 캐시, self-hosted macOS Runner, Linux 쪽 Android·테스트 job을 분리하는 방법입니다. Flutter 프로젝트의 진짜 병목은 Dart가 아니라 iOS 빌드 체인의 Mac 종속입니다: pod install → xcodebuild archive → 서명 → TestFlight 업로드. 이 전체는 Mac 없이는 끝나지 않습니다. 우리 팀은 전형적인 크로스플랫폼 분업: Android는 Windows/Linux CI, Flutter iOS CI는 랙의 전용 Mac mini에 고정. 네이티브 Xcode 혼합이라면 느린 Xcode 빌드를 클라우드로, Runner 등록·TCO는 자체 macOS Runner를 보세요.
1) Flutter iOS CI가 전용 Mac을 쓰는 이유
「하나의 코드베이스」가 풀어 주는 건 비즈니스 로직 재사용이지, 빌드 평면 재사용이 아닙니다. iOS 산출물은 여전히 전체 Xcode 파이프라인을 탑니다. ubuntu-latest에서는 Flutter iOS CI가 성립하지 않습니다. GitHub 호스티드 macOS는 분당 비용이 높고 큐도 들쭉날쭉하며, job마다 캐시가 비면 flutter build ipa는 다시 15–25분대로 돌아갑니다. Flutter iOS CI/CD를 영속 환경의 Mac mini에 두면 얻는 핵심은 Flutter/Xcode 버전 고정, 3계층 캐시 재사용, 24/7 절전 없음——Android와 iOS 릴리스 리듬을 진짜로 분리할 수 있습니다.
flutter test, build apk는 Linux에 둡니다. Flutter iOS CI 전용기는 iOS 통합 빌드·서명 단계만.2) Flutter iOS CI 클라우드 아키텍처
아래는 운영 중인 Flutter iOS CI/CD 토폴로지입니다. 개발자 전원이 로컬 Mac을 가질 필요는 없고, iOS 패키지는 self-hosted Runner가 만듭니다.
Flutter iOS CI 클라우드 아키텍처
로컬: Dart · Android · git push
ubuntu-latestflutter test · build apk
labels: macos, flutter-ios
fvm flutter build ipapod install · xcodebuild archive
Mac mini 영속 캐시 계층(Flutter iOS CI 가속 핵심)
~/.pub-cache- CocoaPods cache
- DerivedData
3) Flutter iOS CI 운영 3가지 패턴
| 패턴 | 로컬 / 기타 CI | 클라우드 M4 Mac | 적합 |
|---|---|---|---|
| A. iOS 빌드만 클라우드 | 일상 flutter run, Android 패키징 | build ipa, 스토어 업로드 | 하이브리드(Win/Linux + 실기기) |
| B. Flutter 풀 리모트(드묾) | 에디터 + git만 | 시뮬레이터, flutter doctor 전부 통과 | 로컬 Mac 없음, VNC 지연 허용 |
| C. 클라우드 Mac = iOS Runner | PR로 Linux job | 라벨 macos self-hosted Runner | 잦은 릴리스, 강한 캐시 |
우리는 A + C: 머지 후 Linux에서 flutter test; Flutter iOS CI는 tag/main 머지 시에만 Mac mini Runner. B는 단기 Mac 없이 붙일 때, 장기는 Runner화 권장.
4) Flutter iOS CI 환경 체크리스트(Mac mini 1회 설치)
- Xcode + CLT:
ios/Podfile·Flutter 채널 최소 버전에 맞춤. 설치 후sudo xcodebuild -license accept. - Flutter SDK:
fvm또는~/flutter고정. 저장소에.fvmrc로 팀·CI 버전 통일. - CocoaPods:
gem install cocoapods또는 Homebrew. 대형 저장소는Podfile.lock커밋. - 서명: Fastlane Match 또는 Xcode 자동 서명. p12·프로비저닝은Git 금지, 암호화 저장소 또는 CI Secret.
- 네트워크: Git 원격·pub.dev/CDN에 가까운 리전. 사내 Git은 VPN/전용선. 출구 안내는 도움 센터.
새 머신에서 첫 flutter doctor -v 출력을 Wiki에 붙여 두세요——이후 장애는 이 「골든 스냅샷」부터 대조합니다.
5) CocoaPods 캐시 최적화: Flutter iOS CI 가속 핵심(Before/After 포함)
중간 규모 Flutter 저장소(플러그인 40+, Release IPA)를 3환경에서 각 3회, 중앙값 비교——Flutter iOS CI 차이는 거의 「캐시가 Mac mini에 남는가」뿐입니다.
| 환경 | 첫 빌드(콜드) | 두 번째(캐시 히트) |
|---|---|---|
| 로컬 MacBook Air M2(8GB, 덮개/스로틀) | 18–25 min | 12–15 min |
GitHub 호스티드 macos-latest(커스텀 캐시 없음) | 16–22 min | 14–18 min |
| Mac mini M4(16GB) · Flutter iOS CI + 3계층 캐시 | 15–20 min | 4–8 min |
두 번째 빌드를 12분+에서 한 자릿수로 줄인 건 아키텍처의 3계층 영속화——Flutter iOS build on Mac mini를 「빌드 가능」에서 「CI로 쓸 만함」으로 올린 분기점입니다.
PUB_CACHE/~/.pub-cache: Runner 환경변수로 경로 고정, job 간 Dart 의존 재사용.- CocoaPods cache +
ios/Pods:Podfile.lock불변 시pod install스킵. 변경 시 증분만. - DerivedData:
~/DerivedData고정. Xcode 메이저 업 전까지clean안 함.
6) Flutter iOS CI 명령 흐름(SSH 또는 Runner 스크립트)
저장소 루트에서 실행. Flutter 버전은 프로젝트 고정판으로 바꾸세요:
cd ~/work/my_flutter_app
export PUB_CACHE=$HOME/.pub-cache
fvm flutter pub get
cd ios && pod install --repo-update && cd ..
fvm flutter build ipa --release \
--export-options-plist=ios/ExportOptions.plist
GitHub Actions에는 위 단계를 release-ios.yml에 넣고 runs-on: [self-hosted, macos, flutter-ios]. TestFlight 업로드 시 Mac mini 출구 대역·App Store Connect 경로가 집 Wi‑Fi보다 안정적인 경우가 많습니다.
7) GitHub Actions: Flutter iOS CI/CD workflow 분리
workflow 2개로 나누거나(matrix if로 분기) 권장:
test-android.yml:ubuntu-latest에서flutter test,build apk.release-ios.yml:runs-on: [self-hosted, macos, flutter], tag 또는main머지 시만build ipa+ 업로드.
클라우드 Mac에서 VNC 디버깅과 Runner를 같이 쓰면 빌드 전용 시스템 사용자를 분리하세요——수동 편집과 자동 checkout 충돌 방지. 동시성·디스크는 Runner TCO 참고. Windows 주력 전체 경로는 Windows + 클라우드 Mac——Flutter도 로컬에 Xcode가 없다는 점은 같습니다.
8) 자주 묻는 문제: Flutter iOS CI가 멈추는 이유(how/fix)
Google에서 많이 보는 why/how/fix/error 검색에 대응——팀 Wiki 진입점입니다.
pod install이 10분 넘게 걸리는 게 정상인가요?
Why: Runner가 매번 임시 디렉터리에 checkout하고 CocoaPods 다운로드 캐시가 마운트되지 않음. 또는 pod install --repo-update가 기본 스크립트에 포함.
Fix: WORK 경로 고정. Podfile.lock hash 비교 후 불변 시 스킵. --repo-update는 주간 유지보수 job으로 이동.
xcodebuild archive 실패 / exit code 65
Why: Flutter·Xcode 버전 불일치, DerivedData 손상, native plugin이 현 SDK 미지원.
Fix: CI 시작에 fvm flutter doctor -v 로그 보관. DerivedData 삭제 후 재실행. Xcode Release Notes로 채널 확인.
signing failed / 프로비저닝 불일치
Why: 키체인에 p12 미임포트, bundle id·프로파일 불일치, Match 저장소 권한 만료.
Fix: Mac mini Runner 전용 키체인. Fastlane Match 갱신. 필요 시 VNC로 「항상 신뢰」——Flutter iOS CI 최빈 「코드 외」 실패.
DerivedData 폭주로 디스크 가득
Why: 다중 브랜치 병렬 job, 오래된 Archives 미삭제, Flutter 버전 공존.
Fix: cron으로 ~/Library/Developer/Xcode/Archives 정리. Runner 동시성 1–2. 디스크 ≥512GB 또는 정기 확장.
Flutter 버전 불일치로 plugin 컴파일 오류
Why: 개발자 로컬 fvm 미사용, CI는 다른 채널.
Fix: .fvmrc 커밋. CI는 fvm install && fvm flutter pub get 통일. PR 템플릿에 「Flutter SDK 변경」 체크 추가.
9) 전용 Mac mini로 Flutter iOS CI를 쓸 시점
| 상황 | 동료 Mac 빌리기 / 중고 구매 | 클라우드 M4 Mac mini |
|---|---|---|
| 월 iOS 릴리스 ≥ 4회 | 조율 비용 큼 | 권장 고정 빌드기 + 캐시 |
| 팀에 Mac 없음, Flutter만 | 지속 어려움 | 일일 대여로 실제 build ipa 시험 |
| 사무실 Mac mini 24/7 보유 | 자체 구축으로 충분 | 클라우드 Mac은 DR·피크 |
| 다수 iOS 플러그인 네이티브 튜닝 | 로컬 Mac 편함 | 클라우드 release, 로컬 debug |
10) FAQ(Flutter iOS CI/CD)
Flutter iOS CI와 네이티브 iOS CI 차이는?flutter pub get·plugin 코드 생성 한 겹 더, 버전은 .fvmrc와 일치 필수. Xcode 쪽은 Pods + archive 동일.
Codemagic/호스티드 macOS와 비교?
호스티드는 빠른 검증용. 두 번째 빌드를 10분 안에 안정·강한 캐시가 필요하면 Flutter iOS build on Mac mini + self-hosted Runner가 더 통제 가능.
첫 Flutter iOS CI 통과까지?
숙련자 기준 약 1 영업일: Xcode/Flutter 설치, Runner 등록, 서명, build ipa → TestFlight 1회.
엔지니어링 결론: Flutter iOS CI 최소 구성(MVF)
Flutter 프로젝트가주 1회 이상 iOS를 배포하고 팀에 Windows/Linux 주력이 있다면, 현실적인 최소안은 다음과 같습니다:
- 16GB Mac mini M4 1대(물리 또는 전용 클라우드)로 Flutter iOS CI 전담;
- self-hosted macOS Runner 등록, Linux
flutter test/build apk와 workflow 분리; .pub-cache, CocoaPods cache, DerivedData 3계층 영속화;- 목표: 48시간 내 실제 저장소로 첫
flutter build ipa→ TestFlight, 이후 동시성·DR용 2대 검토.
동일 MVF로 2차 빌드를 4–8분대로 압축(5절 표 참고). 전용 Mac mini·SSH 인도는 요금·도움 센터에서 릴리스 빈도로 계산——먼저 pipeline 검증, 확장은 그다음.