팀 규모가 커지면 "누구 컴퓨터에서만 빌드가 되는가"가 그대로 병목이 되곤 합니다. iOS 프로젝트의 CI/CD를 Kvmzen의 Mac mini M4 클라우드 호스트로 전면 이전한 뒤, 빌드 대기 시간이 눈에 띄게 줄었고 서명 사고도 거의 사라졌습니다. 이 글은 파이프라인을 처음부터 구축하며 겪은 시행착오와, 최종적으로 정착한 Fastfile과 캐시 전략을 정리한 것입니다.
클라우드 Mac mini M4를 CI/CD에 선택한 이유
Xcode 빌드는 "연산 집약적이면서 OS 버전에 강하게 의존"하는 대표적인 작업이라, 로컬 Runner를 장기간 안정적으로 유지하기가 쉽지 않습니다. 클라우드 Mac 노드를 선택한 이유는 크게 세 가지입니다.
- 일관된 스펙: 이미지에 Xcode와 커맨드라인 도구 버전을 고정해, 누군가 임의로 OS를 업데이트해서 "그 사람 컴퓨터에서만 빌드된다"는 상황을 막습니다.
- 탄력적인 확장: 배포 전 회귀 테스트 때는 노드를 임시로 여러 개 병렬로 띄우고 끝나면 바로 반환할 수 있어, 피크를 위해 하드웨어를 상시 확보할 필요가 없습니다.
- 24시간 상시 운영: M4 칩은 대기 전력이 낮아 야간 빌드나 정기 보안 스캔을 상시로 돌려도 전기료나 소음 걱정이 적습니다.
처음 저질렀던 실수는 "클라우드 호스트"를 그저 원격 모니터로만 여기고 수동 패키징에만 쓴 것입니다. 진짜 효과는 CI 트리거 체인에 연결한 뒤부터 나타났습니다.
CI/CD 파이프라인 설계
단계 구분
전체 배포 파이프라인을 네 단계로 나눴습니다.
- 코드 체크아웃과 의존성 해석(SPM / CocoaPods)
- 단위 테스트와 정적 분석(병렬 실행)
xcodebuild archive아카이브와 서명- TestFlight 업로드 및 당직 채널 알림
훅과 트리거 조건
main 브랜치에 병합될 때마다 1-2단계가 자동으로 실행되고, release/* 태그가 붙어야만 3-4단계가 실행되어 매번의 작은 커밋이 서명 쿼터를 소모하지 않습니다. Fastlane 스크립트를 로컬에서 디버깅할 때는 보통 Ctrl+C로 전체 실행을 한번 중단하고 실패한 단계만 다시 돌리는데, 대기 시간을 꽤 절약해 줍니다.
Fastfile 예시
lane :ci_archive do
cocoapods
run_tests(scheme: "Kvmzen")
build_app(
scheme: "Kvmzen",
export_method: "app-store",
output_directory: "./build"
)
upload_to_testflight(skip_waiting_for_build_processing: true)
end
스크립트 자체는 복잡하지 않지만, 핵심은 동일한 이미지 베이스라인에서 매번 같은 결과가 나와야 한다는 점입니다. 그래서 빌드 로그에 항상 이미지 버전을 함께 기록해, 실패가 환경 문제인지 코드 문제인지 나중에 추적할 수 있게 했습니다.
캐시와 의존성 관리
캐시를 제대로 설정하지 않으면 클라우드 빌드의 속도 이점이 의존성 다운로드 시간에 그대로 잡아먹힙니다. 3개월간 운영하며 정리한 비교표입니다.
| 캐시 대상 | 캐시 미사용 | 이미지 내장 캐시 사용 |
|---|---|---|
| CocoaPods 설치 | 약 4-6분 | 약 30초 |
| SPM 의존성 해석 | 약 3분 | 약 15초 |
| DerivedData 증분 빌드 | 전체 빌드에 가까움 | 히트율 80% 이상 |
용어를 먼저 정리해두겠습니다.
- DerivedData
- Xcode의 증분 빌드 산출물 캐시 디렉터리로, 빌드 간에 재사용하면 두 번째 이후 컴파일 시간이 크게 줄어듭니다.
- SPM 캐시
- Swift Package Manager가 로컬에 내려받은 패키지 소스와 해석 결과 캐시로, 매 빌드마다 원격 저장소를 다시 가져오지 않게 해줍니다.
- 이미지 베이스라인
- 클라우드 호스트가 부팅 즉시 사용 가능한 시스템 스냅샷으로, 고정된 버전의 Xcode/CLT와 예열된 의존성 캐시를 포함합니다.
자주 발생하는 문제 진단
가장 놓치기 쉬운 부분은 인증서 만료가 즉시 에러로 나타나지 않고, TestFlight 업로드라는 마지막 단계에서야 실패한다는 점입니다. 그때는 이미 파이프라인 전체가 십여 분간 헛돌고 난 뒤입니다. 이제는 1단계에서 인증서 유효기간을 확인해 조기에 실패하고 조기에 알림을 보냅니다.
초기에는 "매일 밤 캐시를 전부 지우고 처음부터 다시 빌드"하는 무식한 방법으로 캐시 부패를 막았지만, 이후 이미지 버전 번호로 캐시를 무효화하는 방식(버전이 같으면 재사용, 이미지가 바뀔 때만 초기화)으로 바꿔 속도를 유지하면서도 오염된 캐시가 몇 주씩 숨어 있다가 문제를 일으키는 일을 막았습니다.
"한 번의 초록불 CI를 믿지 말고, 열 번 연속 초록불인 CI를 믿어라." — 당직 매뉴얼에서 반복해서 강조하는 문구로, 새 클라우드 노드로 이전한 뒤 첫 2주 동안 특히 유념하고 있습니다.
자주 묻는 질문
클라우드 Mac mini는 자체 구축한 Mac Runner에 비해 보안성이 어떻습니까?
클라우드 쪽은 기본 OS 이미지의 베이스라인을 통합 관리하지만, 서명 인증서와 프로비저닝 프로파일에 대한 접근 권한 관리는 여전히 팀의 책임입니다. 인증서는 런타임 환경 변수로만 주입하고 이미지에 굽지 말며, 노드를 반납하기 전에는 키체인을 반드시 비우세요.
무료 Xcode Cloud 쿼터로 이 전체 구성을 대체할 수 있나요?
소규모 팀이라면 Xcode Cloud만으로도 충분합니다. 다만 병렬 빌드 수, 커스텀 스크립트, 프로젝트 간 캐시 공유에 대한 요구가 커지면 클라우드 Mac 노드 위에 직접 구축한 파이프라인이 더 유연하고 비용 상한도 관리하기 쉽습니다.
여러 프로젝트가 같은 클라우드 노드 풀을 공유할 때 인증서는 어떻게 분리해야 하나요?
프로젝트마다 별도의 키체인 또는 키체인 프로필을 두고, 빌드 시작 시 가져오고 종료 시 비우는 방식을 권장합니다. 이렇게 하면 서로 다른 프로젝트의 인증서가 같은 시스템 키체인을 오염시키는 것을 막을 수 있습니다.
CI/CD를 M4 Mac mini에 올리면 정말 손이 덜 갑니다
이 글에서 다룬 Xcode, Fastlane, CocoaPods, SPM은 모두 macOS에서 네이티브로 동작하며 가상머신이나 호환 계층이 필요 없습니다. Mac mini M4의 통합 메모리 아키텍처는 서명·아카이브·업로드처럼 I/O와 연산이 섞인 작업이 서로를 지연시키지 않게 해주고, 약 4W의 대기 전력 덕분에 24시간 상시 노드의 전기료도 거의 무시할 수 있습니다.
자체 Mac Runner를 운영하는 것과 비교하면, 클라우드 노드는 냉각 설비, OS 업데이트 일정, 장애 시 하드웨어 교체 같은 운영 부담을 통째로 넘겨줄 수 있어 팀이 파이프라인 자체의 품질에 집중할 수 있습니다.
"결국 누구 컴퓨터에서만 빌드되는가"로 고민하고 있다면, 지금이 CI를 클라우드 Mac mini M4로 옮길 좋은 시점입니다 — Kvmzen 요금제 보기, 몇 분 안에 첫 빌드 노드를 띄울 수 있습니다.
