2026년 독점 원격 Mac iOS/macOS CI에서 SwiftPM Package.resolved 커밋 여부
iOS CI / DevOps 2026-04-25

2026년 독점 원격 Mac iOS/macOS CI: SwiftPM의 Package.resolved는 정말 커밋해야 할까?——재현 가능한 빌드, 캐시 예열, CI 잠금 드리프트와 SSH 파이프라인 의사결정 매트릭스 + FAQ

의존성 그래프를 한 점에 고정할지, 아니면 CI에서 매번 해석기가 허용 범위 안에서 다시 고를지는 팀마다 답이 갈립니다. 이 글은 독점 원격 Mac과 SSH 기반 파이프라인이라는 현실 제약 위에서 Package.resolved, 캐시 예열, 잠금 드리프트를 나누고 런북에 붙일 수 있는 의사결정 매트릭스와 FAQ를 제공합니다. 자체 호스팅 러너에서 캐시 볼륨·동시성을 설계할 때는 GitLab Runner(macOS)와 GitHub Actions 자체 호스팅 Runner 비교 — 독점 원격 Mac 노드 글의 캐시·계정 격리 축을 함께 보세요.

1. 무엇을 잠그는가: Package.resolved의 역할

Package.resolved는 SwiftPM이 한 번 해석한 뒤의 정확한 리비전·체크섬 정보를 담아, 같은 의존성 그래프를 다시 만들기 위한 스냅샷입니다. Package.swift의 버전 범위와는 층이 다릅니다: 범위는 “어디까지 허용할지”, resolved는 “이번에 어디에 박혔는지”입니다.

Xcode 프로젝트 + SPM 통합이면 흔히 xcworkspace/xcshareddata/swiftpm/Package.resolved에 생깁니다. 순수 Swift Package 저장소는 루트 Package.resolved가 중심입니다. CI 스크립트가 둘 중 하나만 백업하면 “CLI에서는 되는데 Xcode에서는 다시 해석” 같은 불일치가 반복됩니다.

2. 재현 가능 빌드: 앱 저장소는 거의 항상 커밋

배포 가능한 앱·펌웨어·내부 도구 저장소에서는 업계적으로 Package.resolved를 커밋하는 편이 기본에 가깝습니다. PR·메인·로컬·CI가 같은 해석 결과 위에서 diff를 보게 되고, “CI에서만 터지는” 회귀를 볼 때 해석기가 다른 호환 버전을 택했는지를 먼저 제거할 수 있습니다.

외부에 배포하는 Swift Package 라이브러리는 보통 커밋하지 않거나 resolved를 로컬·샘플 용도로만 둡니다. 라이브러리가 약속하는 것은 Package.swift의 호환 범위인데, 단일 resolved가 그것을 대신한다고 오해하기 쉽기 때문입니다. 여러 Swift 툴체인·플랫폼을 매트릭스로 돌리는 라이브러리 CI라면 resolved 한 장보다 매트릭스와 스펙이 진실 공급원에 가깝습니다.

3. 캐시 예열: resolved 커밋 ≠ 다운로드 절약

독점 원격 Mac의 강점은 영속 디스크입니다. SourcePackages, Git 미러, 사내 registry 프록시를 머신 단에서 예열할 수 있습니다. 그러나 캐시 히트는 키(해석 결과, 툴체인 버전, registry 설정, 네트워크 경로 등)에 달려 있고, “resolved 파일이 있다”는 사실만으로 자동 저장되지는 않습니다.

실무적으로 resolved 커밋은 해석의 불확실성을 줄이고, 캐시 정책은 fetch/checkout 반복을 줄입니다. SSH 파이프라인에서는 -clonedSourcePackagesDirPath나 팀이 정한 DerivedData·SourcePackages 경로를 고정하고, Xcode 메이저 업그레이드·registry 이전·보안 사건 때만 캐시를 비우도록 런북에 적어 두는 것이 안전합니다. 러너 풀·캐시 마운트 설계 맥락은 앞서 인용한 GitLab/GitHub 자체 호스팅 비교 글과 잘 맞물립니다.

4. CI 잠금 드리프트: 커밋해도 흔들릴 수 있다

흔한 원인은 Xcode·Swift 툴체인 업그레이드, 범위 기반 버전에 새 태그가 생긴 뒤 resolved를 덮어쓰는 CI 단계, 미러·registry 메타데이터 변화, 여러 workspace·엔트리에서 해석 경로가 어긋나는 것입니다. 독점 러너를 오래 고정하면 문제가 한 번에 터지는 이벤트로 쌓이고, 클라우드 러너는 반대로 드리프트가 잦지만 분산됩니다.

완화 전략: 툴체인 버전을 릴리스 게이트에 명시하고, 앱 저장소는 CI에서 resolved가 의도치 않게 바뀌지 않았는지 검사합니다. 물리적으로 노드를 고정해 두는 이유와 트레이드오프는 베어메탈 Mac mini와 전용 하드웨어 주제에서도 같은 맥락으로 읽을 수 있습니다.

5. 의사결정 매트릭스(독점 원격 Mac + SSH)

시나리오 Package.resolved 한 줄 요약
iOS·macOS 앱 메인 저장소 커밋 재현성·PR에서 의존성 변경 리뷰 용이
외부 공개 SwiftPM 라이브러리 보통 무시 호환 면을 단일 해석으로 대체하지 않기
모노레포·다중 App·다중 workspace 엔트리마다 커밋 CLI와 Xcode가 각자 해석하는 것 방지
Fork·패치 의존성 + 잦은 rebase 커밋 + 엄격한 머지 규칙 충돌이 곧 의존성 그래프 충돌을 드러냄
공급망 감사·SBOM과 연동 커밋 + CI 검증 허용 목록·무결성 체크에 resolved가 근거가 됨

6. SSH 독점 파이프라인에서의 실무 팁

  • 명시적 해석 단계: xcodebuild 전에 -resolvePackageDependencies(또는 동등 래퍼)를 두고, 실패 시 로그를 남깁니다.
  • 비대화형: SSH 세션에서 키체인·자격 증명 팝업이 뜨지 않게 사전 주입·프로파일을 고정합니다.
  • 동시 job: 같은 사용자·같은 DerivedData·SourcePackages 경로를 여러 job이 나눠 쓰면 flaky가 잘 납니다. 사용자/경로 분리나 직렬화 중 하나를 택합니다.
  • 업그레이드 데이: Xcode 메이저 전환 후에는 한 번 콜드 빌드로 검증한 다음 캐시를 되살립니다.

7. FAQ

Q1: CI에서 “package resolution failed”가 나면 무엇부터 보나요?

툴체인 버전, 네트워크·registry, Git 자격 증명, 그리고 Package.swift와 resolved가 가리키는 해석 엔트리(Xcode 프로젝트 vs 패키지 루트)가 같은지입니다.

Q2: resolved 머지 충돌은 어떻게 푸나요?

JSON을 손으로 합치지 말고, 메인을 합친 뒤 공식 해석 플로를 한 번 돌려 새 resolved를 생성·커밋합니다. 충돌 자체가 의존성 그래프가 동시에 바뀌었다는 신호이므로 리뷰가 필요합니다.

Q3: 커밋하지 않으면 더 빨라지나요?

때로는 머지 고민이 줄지만, 대신 설명 불가능한 CI 드리프트가 생깁니다. 장기 가동 독점 러너에서는 “깨끗한 다른 머신으로 재현”으로 자가 진단하기도 어렵습니다.

Mac mini에서 의존성 잠금을 ‘기본적으로 맞게’

의존성 해석과 컴파일은 모두 CPU·디스크·I/O가 섞인 부하입니다. Apple 실리콘의 통합 메모리와 대역폭은 큰 SPM 그래프를 해석하고 증분 빌드할 때 병목을 덜 만듭니다. macOS는 Xcode·Swift 공식 툴체인의 1급 플랫폼이라 크로스 플랫폼 에뮬레이션 층에서 생기는 변수가 적습니다. Gatekeeper·SIP 등 보안 스택은 공격 표면을 줄이는 데 도움이 되고, 팬이 거의 없는 Mac mini는 대기 전력이 낮아 7×24로 두기 쉬워 총소유비용 계산도 단순해집니다.

이 글의 캐시 예열·SSH 비대화형 해석·job 격리를 안정적으로 굴리려면 조용하고 전력 효율 좋은 Mac mini M4급 노드를 CI의 한 축으로 두는 구성이 여전히 현실적인 출발점입니다. 지금 팀 인프라에 맞는 옵션을 보려면 SSHMac 홈으로 이동해 구성을 확인하세요.

원격 Mac 빌드 인프라

독점 Mac mini CI 친화

Apple 실리콘 영속 캐시 SSH 네이티브
재현 빌드는 의존성·환경을 함께 고정할 때 가장 잘 유지됩니다.
옵션 보러 가기