하나의 클라우드 Mac에서 안정화 브랜치, 일상 개발 브랜치, 업그레이드 검증 브랜치를 동시에 운영할 때 가장 놓치기 쉬운 변수는 코드가 아니라 현재 활성화된 Xcode입니다. 어떤 작업이 전역 설정을 변경하면 이후 파이프라인이 다른 Swift 컴파일러나 SDK를 조용히 사용하게 될 수 있습니다. 그 결과 로컬에서는 빌드되지만 원격에서는 실패하거나, 동일한 커밋이 다음 날 서로 다른 빌드 결과를 생성하는 문제가 발생합니다.
도구 체인을 프로젝트 의존성으로 먼저 다루기
Xcode를 단순히 머신에 설치된 앱으로 취급해서는 안 됩니다. 언어 런타임처럼 사용할 버전을 명확히 기록해야 합니다. 새 설치 패키지가 기존 디렉터리를 덮어쓰지 않도록 버전이 포함된 디렉터리를 유지하는 것이 좋습니다.
/Applications/Xcode_16.1.app
/Applications/Xcode_16.2.app
/Applications/Xcode_16.3.app
먼저 현재 설치된 버전과 전역으로 선택된 경로를 확인합니다.
find /Applications -maxdepth 1 -name 'Xcode*.app' -print
xcode-select --print-path
xcodebuild -version
xcrun --find swift
swift --version
기록에는 최소한 Xcode 버전, 빌드 번호, Swift 버전, 프로젝트에서 사용하는 SDK가 포함되어야 합니다. 단순히 “최신 버전 사용”이라고 적어 두면 과거 빌드를 재현할 수 없고, 실패 원인이 코드인지 도구 체인 변경인지도 판단하기 어렵습니다.
버전 고정의 목적은 업그레이드를 거부하는 것이 아니라, 업그레이드를 관찰하고 되돌릴 수 있는 엔지니어링 변경으로 만드는 데 있습니다.
범위에 따라 Xcode 선택하기
선택 방식은 작업 범위에 맞춰 결정해야 합니다. 전역 기본값은 수동 유지보수에 적합하고, 작업별 환경 변수는 자동화에 더 적합합니다.
| 방식 | 적용 범위 | 권장 상황 | 동시 실행 위험 |
|---|---|---|---|
xcode-select |
호스트 전체 | 1인 유지보수, 통일된 기본 버전 | 다른 작업에 영향을 줌 |
DEVELOPER_DIR |
현재 프로세스와 하위 프로세스 | CI, 스크립트, 병렬 프로젝트 | 낮음 |
| 명령 앞에서 임시 할당 | 단일 명령 | 빠른 검증 | 낮음 |
전역 기본값을 변경해야 한다면 다음 명령을 실행할 수 있습니다.
sudo xcode-select --switch /Applications/Xcode_16.2.app/Contents/Developer
CI에서는 이 명령을 반복해서 실행하지 마세요. 대신 작업 시작 시 다음과 같이 설정합니다.
export DEVELOPER_DIR="/Applications/Xcode_16.2.app/Contents/Developer"
xcodebuild -version
xcrun --sdk iphoneos --show-sdk-version
DEVELOPER_DIR는 하위 프로세스에도 전달되므로 이후 실행되는 xcrun, swift, xcodebuild가 모두 동일한 Developer 디렉터리에서 도구를 찾습니다. 각 파이프라인은 전역 상태를 두고 충돌하지 않으면서 서로 다른 경로를 개별적으로 설정할 수 있습니다.
사전 점검 스크립트로 조용한 드리프트 막기
로그에 버전을 출력하는 것만으로는 충분하지 않습니다. 사전 점검은 버전이 일치하지 않을 때 즉시 작업을 종료해야 합니다. 그래야 십여 분 동안 컴파일한 뒤에야 문제가 드러나는 상황을 피할 수 있습니다. 다음 스크립트는 Xcode 16.2를 고정하고 앱 디렉터리, 실제 버전, SDK, Swift 경로를 확인합니다.
#!/bin/bash
set -euo pipefail
XCODE_APP="/Applications/Xcode_16.2.app"
EXPECTED_VERSION="16.2"
test -x "$XCODE_APP/Contents/Developer/usr/bin/xcodebuild"
export DEVELOPER_DIR="$XCODE_APP/Contents/Developer"
ACTUAL_VERSION="$(xcodebuild -version | awk 'NR == 1 {print $2}')"
test "$ACTUAL_VERSION" = "$EXPECTED_VERSION"
echo "developer=$DEVELOPER_DIR"
echo "xcode=$ACTUAL_VERSION"
echo "swift=$(xcrun --find swift)"
echo "sdk=$(xcrun --sdk iphoneos --show-sdk-version)"
xcodebuild -checkFirstLaunchStatus
스크립트는 ci/verify-toolchain.sh처럼 저장소에 넣어 버전 요구 사항도 코드 리뷰 대상이 되게 하세요. 업그레이드할 때 스크립트와 프로젝트 문서를 함께 수정하면, 호스트에서 수동으로 수행한 작업보다 변경 이력을 더 안정적으로 남길 수 있습니다.
프로젝트에서 특정 SDK 버전을 요구한다면 정확한 검증 조건을 추가할 수 있습니다. 같은 Xcode 계열에서도 버전에 따라 포함된 SDK가 다를 수 있으므로 Xcode의 주 버전만 보고 SDK를 추정해서는 안 됩니다.
빌드 명령도 명시적으로 정의하기
사전 점검을 통과한 다음에는 고정된 진입점으로 빌드를 실행합니다.
export DEVELOPER_DIR="/Applications/Xcode_16.2.app/Contents/Developer"
xcodebuild \
-workspace Example.xcworkspace \
-scheme Example \
-configuration Release \
-derivedDataPath "$PWD/.derived-data" \
CODE_SIGNING_ALLOWED=NO \
build
여기서는 workspace, scheme, 구성, Derived Data 경로를 모두 명시합니다. CODE_SIGNING_ALLOWED=NO는 서명이 필요 없는 컴파일 검증에만 적합합니다. 아카이브나 기기 빌드에는 프로젝트에서 이미 사용 중인 안전한 서명 절차를 적용해야 하며, 이 매개변수를 그대로 사용해서는 안 됩니다.
새 버전은 병렬로 검증한 뒤 전환하기
새 Xcode를 설치한 뒤에도 기존 버전을 먼저 유지하고, 업그레이드 검증은 별도 작업으로 실행하세요. 최초 준비 작업은 관리자가 다음과 같이 수행할 수 있습니다.
sudo DEVELOPER_DIR="/Applications/Xcode_16.3.app/Contents/Developer" \
xcodebuild -runFirstLaunch
그런 다음 동일한 커밋을 기존 버전과 새 버전에서 각각 실행해 컴파일 성공 여부, 테스트 수, 경고 변화, 산출물 아키텍처, 아카이브 메타데이터를 비교합니다. 처음부터 기존 디렉터리를 삭제하면 의존성이나 컴파일 매개변수가 호환되지 않을 때 빠르게 되돌릴 수 없습니다.
업그레이드 검수 과정에서는 프로젝트 형식이 자동으로 다시 작성되었는지도 확인해야 합니다. 새 Xcode가 프로젝트 파일을 수정했다면 해당 변경은 별도 커밋으로 분리해 검토하고 비즈니스 코드와 섞지 마세요. 이렇게 해야 도구의 자동 마이그레이션, 의존성 업데이트, 실제 기능 변경을 구분할 수 있습니다.
흔한 실수와 배포 전 점검
첫 번째 실수는 xcode-select만 전환하고 작업 환경에 남아 있는 DEVELOPER_DIR를 확인하지 않는 것입니다. 환경 변수의 우선순위가 더 높기 때문에 로그에 표시된 전역 경로가 작업에서 실제 사용하는 경로와 같다고 볼 수 없습니다. 두 번째는 앱 이름을 그대로 Xcode.app으로 지정하는 것입니다. 업그레이드가 기존 앱을 덮어쓰면 과거 작업에서 어떤 버전을 사용했는지 확인할 수 없습니다. 세 번째는 컴파일러만 검증하고 SDK와 xcrun 경로는 확인하지 않아 의도하지 않은 도구가 섞이는 경우입니다.
배포하기 전에 다음 순서로 확인하세요.
- Xcode 앱 디렉터리에 명확한 버전 번호가 포함되어 있고 기존 버전도 계속 사용할 수 있습니다.
- 기대하는 버전을 저장소에 기록하며 사람의 기억에 의존하지 않습니다.
- 각 CI 작업에서
DEVELOPER_DIR를 개별적으로 설정합니다. - 사전 점검에서 Xcode, Swift, SDK, 도구 경로를 모두 확인합니다.
- 빌드 명령에 workspace, scheme, 구성을 명시합니다.
- 새 버전으로 컴파일, 테스트, 산출물 검사를 통과한 뒤 기본값으로 설정합니다.
- 병렬 작업에서는 전역
xcode-select를 변경하지 않습니다.
이 단계를 완료하면 클라우드 Mac의 도구 체인 상태를 로그를 통해 추적할 수 있습니다. 빌드 결과에 차이가 생겼을 때 먼저 버전 드리프트를 배제한 뒤 의존성, 코드, 작업 구성을 조사할 수 있으므로 여러 변수 사이에서 맹목적으로 시행착오를 반복하지 않아도 됩니다.
자주 묻는 질문
서로 다른 Xcode를 사용하는 CI 작업을 동시에 실행할 수 있나요?
가능합니다. 시스템 전체의 xcode-select를 바꾸지 말고 각 작업에서 DEVELOPER_DIR를 별도로 설정하면 프로세스마다 지정된 도구 체인을 사용합니다.
xcodebuild -version만 확인하면 충분한가요?
충분하지 않습니다. DEVELOPER_DIR, SDK와 Swift 버전, xcrun이 선택한 실행 파일 경로까지 검사해야 혼합된 도구 체인을 찾을 수 있습니다.
새 Xcode를 기본 버전으로 바꾸기 전에 무엇을 확인해야 하나요?
버전이 포함된 이름으로 설치하고 최초 실행 준비를 마친 뒤, 기존 버전을 유지한 상태에서 사전 점검과 빌드 및 테스트를 통과시켜야 합니다.
독립형 Mac mini 물리 노드가 필요하신가요?
사용 가능한 구성, 노드 및 고정 결제 주기를 확인하고, 글의 단계를 지속적으로 사용할 수 있는 클라우드 Mac 환경에 적용해 보세요.