2026 공유 원격 Mac CI에서 SwiftPM과 CocoaPods Package.resolved · Bundler 격리 · DerivedData 디스크 거버넌스

공유 또는 전용 원격 Mac에서 SwiftPMCocoaPods를 함께 실행하면 Package.resolved, Gemfile.lock, DerivedData가 디스크 한도와 충돌하고, 동시 작업이 같은 경로를 밟습니다. 본문은 플랫폼 팀용 승인 체크리스트입니다. 숨은 비용 7가지, SPM 전용·Pods 전용·혼합 스택 비교표, 디스크·캐시 6단계 Runbook, 그리고 재현 빌드, 셀프호스트 Runner, 스냅샷과 장기 노드 가이드와의 연계 방법을 정리합니다.

01

스택을 섞기 전에: 공유 원격 Mac CI에서 잠금 파일과 디스크를 깨뜨리는 일곱 가지 숨은 비용

Linux에서는 의존성 캐시와 빌드 산출물을 볼륨마다 나눕니다. 공유 macOS 호스트에서는 SwiftPM 체크아웃, Pods 트리, DerivedData가 기본적으로 한 홈 아래로 모이기 쉽습니다. 병렬 저장소가 더해지면 CPU보다 먼저 디스크나 inode 압박이 옵니다. 아래 일곱 항목은 사전 검토용입니다.

  1. 01

    Package.resolved와 브랜치 정책 드리프트: 브랜치별 버킷이 없으면 해결은 성공해도 잘못된 아티팩트에 링크할 수 있습니다.

  2. 02

    CocoaPods용 Ruby 생태계 드리프트: 시스템 Ruby, rbenv, Bundler를 섞으면 pod 버전이 달라져 로컬은 녹색인데 CI는 불안정해집니다.

  3. 03

    공유 DerivedData 루트: 여러 작업이 하나의 -derivedDataPath에 쓰면 모듈 캐시와 인덱스가 밟히고, 의존성 손상처럼 보입니다.

  4. 04

    Pods와 SPM의 서드파티 중복: 같은 라이브러리가 두 번 들어가면 다운로드 크기와 해결 시간이 두 배가 되며 아키텍처 검토에서 놓치기 쉽습니다.

  5. 05

    살아 있는 캐시를 지우는 정리 스크립트: 날짜만 기준으로 돌아가는 cron은 대기 중인 작업의 아티팩트까지 지워 연쇄 실패를 만듭니다.

  6. 06

    일관되지 않은 CDN·미러 정책: pod repo update와 SPM 해결이 다른 출구를 쓰면 지터가 CI 불안정으로 관측됩니다.

  7. 07

    디스크 워터마크 계약 부재: 스케줄 중지나 읽기 전용으로 넘어갈 임계값이 없으면 누구도 알기 전에 디스크가 가득 차 복구 비용이 커집니다.

공통 원인은 Linux VPS의 단일 저장소·단일 파이프라인 모델을 멀티 테넌트 Mac으로 가져오는 것입니다. 디스크만 키우는 것이 아니라 이름이 붙은 캐시와 잠금 파일이 필요합니다. 플랫폼 원장에 기록한 뒤 다음 표에서 스택을 수렴할지, 엄격한 디렉터리 경계로 혼합을 받아들일지 결정합니다.

SwiftPM은 Package.resolved에 컴파일러 결합 해결 결과를 담습니다. CocoaPods는 Ruby에 묶이며 기업에서는 보통 Bundler를 씁니다. 최악은 CI에서 대화형 셸로 전역 gem을 바꿔 공유 호스트 전체에 엔트로피를 주입하는 패턴입니다. 커밋된 Gemfile.lock, bundle exec pod install만, 지울 수 있는 파티션의 vendor/bundle을 권장합니다.

재현 빌드와 짝을 이룹니다. 지문 스크립트에는 swift package resolve 체크섬과 pod --version을 넣고 Xcode만 넣지 않습니다. Runner와 짝을 이룹니다. 공유 볼륨에 캐시를 마운트하면 저장소 하위 디렉터리로 범위를 정하고 모두가 쓰는 거대 DerivedData는 피합니다.

네트워크와 CDN 제어는 4절에서 Bundler 옆에 다시 등장합니다. 비교표로 논쟁을 파티션 규칙으로 바꿉니다.

02

SwiftPM 전용, CocoaPods 전용, 혼합: 공유 원격 Mac에서 디렉터리와 리스크 트레이드오프

만능 해법은 없습니다. SPM 전용은 경로가 균일하지만 레거시 ObjC는 여전히 Pods가 필요할 수 있습니다. Pods 전용은 익숙하지만 Ruby와 CDN 운영 비용이 큽니다. 검토에 세 가지 SLA를 적습니다. 해결 반복성, 디스크 피크, 설명 가능한 실패입니다.

차원SwiftPM 우선CocoaPods 우선혼합(레거시에서 흔함)
잠금 파일·반복성Package.resolved가 리졸버·컴파일러 결합을 추적Podfile.lock에 더해 Ruby·Bundler 고정잠금 파일과 캐시가 두 갈래. 중복 의존성과 경로 충돌 주의
디스크 프로파일체크아웃과 SPM 캐시로 비교적 예측 가능Pods가 크고 Ruby 툴체인도 부담피크가 쌓입니다. DerivedData를 명시적으로 버킷화
동시성디렉터리 격리가 쉬워 병렬에 유리pod install이 잠금을 잡을 수 있어 큐에 넣습니다작업 디렉터리를 나누고 한 경로에 두 명의 작성자를 두지 않습니다
운영 레버Xcode 툴체인과 맞고 지문 스크립트에 잘 맞음gem 소스, CDN, Bundler 캐시 정책 유지Runbook이 가장 길지만 실제 앱은 많이 여기에 도달합니다
골든 이미지SPM 캐시 레이어를 사전 예열Ruby와 알려진 Pods를 베이스라인에 구워 넣는 경우가 많음이미지가 커집니다. 툴체인·의존성·가변 레이어로 분리

의존성 층에서 Mac을 VPS처럼 빌리는 것은 예측 가능한 디렉터리 계약과 워터마크를 사는 것이지, 노트북 디스크가 어떻게든 버티길 바라는 것이 아닙니다. SPM과 Pods 캐시를 테넌트로 취급하고 배경 잡음으로 두지 마세요.

엔터프라이즈 빌드 풀을 운영한다면 의존성 해결 작업과 서명·릴리스 작업을 분리합니다. 전자는 공격적인 정리를 견디지만 후자는 실험적 Bundler 상태와 홈을 공유하면 안 됩니다. 스냅샷과 장기 노드와 함께 읽으세요. 혼합 스택을 장기 호스트에서 돌리면 주간 정리와 워터마크가 필요하고, 스냅샷 베이스라인은 Bundler와 SPM 캐시의 레이어 예열에 유리합니다.

아직 Pods를 버릴 수 없다는 결정이면 어떤 서드파티를 SPM 전용·Pod 전용으로 둘지(예: 클로즈드 소스 바이너리)를 규정하고 동일 모듈을 두 경로에 올리지 마세요. 링크 시점에 대가가 나갑니다. 템플릿 저장소에서 최소 Gemfile.bundle/config 조각을 배포해 팀마다 새로 발명하지 않게 합니다.

03

공유 원격 Mac에서 SPM과 Pods를 인수인계 가능한 Runbook으로 만드는 여섯 단계

순서가 중요합니다. 먼저 파티션, 다음 해결, 마지막 캐시 히트입니다. 재현 빌드 지문과 맞춰 의존성 레이어가 둘째의 미기록 환경을 만들지 않게 합니다.

  1. 01

    저장소마다 작업 루트와 브랜치 버킷을 고정:/ci/work/<repo>/<branch-hash>. SPM 체크아웃, Pods, DerivedData를 그 버킷 아래에 두고 기본 ~/Library로 되돌리지 않습니다.

  2. 02

    Bundler와 CocoaPods를 고정: CI 이미지나 부트 스크립트에서는 bundle exec만 쓰고 BUNDLE_PATH를 버킷 안의 vendor/bundle로 둡니다.

  3. 03

    SwiftPM resolve를 빌드와 분리해 관측: resolve에 시간을 재고, 실패 시 .build 인접 로그를 남겨 리졸버 오류와 컴파일 오류를 혼동하지 않습니다.

  4. 04

    작업마다 DerivedData 격리: 위 작업 루트와 함께 xcodebuild에 항상 명시적 -derivedDataPath를 넘깁니다.

  5. 05

    정리 정책을 정의: 워터마크로 트리거하고 유휴 창에서 실행하며 LRU로 퇴거합니다. rm -rf ~/Library 일괄 삭제는 하지 않습니다.

  6. 06

    Runner 라벨을 배선: 무거운 pod install 작업은 Runner 가이드에 따라 전용 라벨로 스로틀하고 컴파일 위주 작업을 굶기지 않습니다.

bash · 작업 버킷 환경 예시(프로젝트에 맞게 조정)
export CI_WORK_ROOT="/ci/work/${REPO_SLUG}/${BRANCH_KEY}"
export DERIVED_DATA="${CI_WORK_ROOT}/DerivedData"
export PODS_ROOT="${CI_WORK_ROOT}/Pods"
export BUNDLE_PATH="${CI_WORK_ROOT}/vendor/bundle"
mkdir -p "$DERIVED_DATA"
bundle config set path "$BUNDLE_PATH"
bundle exec pod install --deployment
swift package resolve
info

팁: 같은 호스트에서 Fastlane 릴리스도 돌린다면 릴리스 버킷과 CI 해결 버킷을 분리해 Bundler와 App Store Connect 도구가 교차 오염하지 않게 합니다.

GitHub Actions 등에서는 의존성 설치와 빌드·테스트를 나누고 캐시 키를 브랜치 이름뿐 아니라 잠금 파일 내용에 묶습니다. 워크스페이스에 SPM과 Pods가 섞이면 xcodebuild -workspace 인자를 고정해 GUI 습관과 CI가 영원히 갈라지지 않게 합니다.

XCTest·시뮬레이터와 함께 읽으세요. 테스트가 컴파일 DerivedData를 재사용하면 읽기·쓰기 범위를 명시합니다. 그렇지 않으면 테스트 정리가 컴파일 캐시를 깨뜨립니다. Linux 컨테이너 볼륨처럼 누가 무엇을 삭제할 수 있는지 문서화합니다.

04

Bundler, CDN, 읽기 전용 폴백: 네트워크 지터를 코드가 깨졌다고 읽지 않기

엔터프라이즈 CocoaPods에서 가장 흔한 운영 사고는 구문 오류가 아니라 해결 시점 네트워크 실패를 애플리케이션 버그로 오해하는 경우입니다. 공유 호스트에는 gem 미러 정책과 Git 전송 정책(HTTPS 대 SSH)을 하나로 두고, 방화벽에서 메타데이터 호스트명을 허용하며, 워크플로에 pod install 타임아웃과 재시도를 박아 넣습니다.

SwiftPM에서는 주요 Xcode 업그레이드 뒤 Package.resolved가 리졸버와 맞는지 봅니다. 카나리 작업에서 swift package resolve와 최소 빌드를 통과한 뒤 본 트래픽을 엽니다. 재현 빌드와 맞추고 잠금 파일 변경은 공유 해결 캐시 무효화가 필요할 수 있음을 경고해 조용한 오염을 막습니다.

warning

경고: 디스크 한계에서도 작업을 계속 받지 마세요. 읽기 전용 폴백(스케줄 중지)으로 전환한 뒤 먼저 정리합니다. 그렇지 않으면 Xcode와 git이 반쯤 쓴 상태를 남겨 짧은 큐 정지보다 비쌉니다.

멀티 리전에서는 CDN 적중률과 해결 지연을 차트로 그립니다. P95가 튀면 팀에 세 번 재시도를 요구하기 전에 미러를 의심합니다. SSH 체크리스트와 짝을 이룹니다. Bundler와 pod 버전은 설정 관리로 배포하고 SSH 즉흥 업그레이드는 피합니다.

05

설계 검토에 붙일 수 있는 참고 수치

임계값은 병렬도와 저장소 크기에 맞게 조정하세요. 아래는 합의용 앵커입니다.

  • 공유 시스템 볼륨 워터마크: 여유 공간 20% 이상을 유지하고, 그 아래에서는 삭제보다 먼저 스케줄을 멈추고 무엇을 지웠는지 감사 로그에 남깁니다.
  • 이중 스택 피크: 큰 iOS 앱에서 DerivedData, Pods, SPM 체크아웃이 합쳐 수십 GB에 달할 수 있습니다. 용량은 단일 빌드 피크가 아니라 가장 무거운 저장소 × 동시 작업 × 안전 계수로 산정합니다.
  • 해결 반복성 프로브: 지문에 swift package resolve 종료 코드, bundle exec pod --version, Podfile.lock 체크섬을 머지 게이트에 기록합니다.

노트북은 슬립하고 툴체인이 흔들려 의존성 문제가 신비롭게 느껴집니다. 순수 Linux는 Apple 공식 iOS 스택을 호스트할 수 없습니다. 해결과 빌드를 전용이면서 항상 켜져 있고 파티션 가능한 원격 Mac으로 옮기면 SwiftPM+CocoaPods를 계약으로 다룰 수 있습니다. 일회성 하드웨어나 불안정한 공유 호스팅과 비교할 때 NodeMini Mac Mini 클라우드 렌탈은 고정 SSH, 명확한 디스크 단, 반복 가능한 노드 프로필을 묶어 플랫폼급 의존성 거버넌스에 적합합니다. 단계 비교는 Mac Mini 대여 가격, 온보딩은 헬프 센터를 이용하세요.

본 Runbook을 사내 의존성 변경 등급과 연결합니다. 잠금 파일 패치·마이너·메이저마다 승인과 캐시 무효 규칙이 달라집니다.

FAQ

FAQ

권장하지 않습니다. 저장소·브랜치 버킷마다 -derivedDataPath를 나누고 Pods와 Bundler vendor 경로도 격리합니다. 그렇지 않으면 동시 작업이 캐시를 밟습니다. 플랫폼 가이드는 헬프 센터를 참고하세요.

Ruby와 Bundler 버전, Gemfile.lock, pod install용 CDN 미러 정책입니다. 이어 공유 호스트마다 정리 워터마크를 둡니다. 디스크 단 비교는 Mac Mini 대여 가격을 확인하세요.

재현 빌드는 Xcode 지문과 키체인을, Runner 글은 라벨과 캐시 마운트를 다룹니다. 본문은 SPM과 Pods를 섞을 때 디렉터리와 디스크 계약을 다룹니다. 기계에서 의존성까지 보려면 세 편을 함께 읽으세요.