동일한 Xcode 프로젝트가 오랫동안 사용해 온 개발 디렉터리에서는 빌드되지만, 새 VPSGit 클라우드 Mac 작업 공간에 복사하면 파일이나 스크립트를 찾을 수 없거나 설정값이 비어 있다는 오류가 발생할 수 있습니다. 이때 기존 캐시부터 옮겨서는 안 됩니다. 먼저 검증해야 할 것은 지정된 커밋, 명시적으로 선언된 툴체인, 파이프라인에서 주입한 매개변수만으로 빌드를 완료할 수 있는지입니다.
클린 체크아웃에서 격리해야 할 상태
DerivedData를 삭제하는 것만으로는 한 종류의 캐시만 배제할 수 있을 뿐, 프로젝트에 로컬 시스템 의존성이 없다는 사실까지 증명할 수는 없습니다. 장기간 사용한 작업 공간에는 커밋되지 않은 파일, 사용자 디렉터리 설정, 전역 설치 도구, Shell 초기화 스크립트, 저장소 외부를 가리키는 절대 경로가 누적되어 있는 경우가 많습니다.
최소한 다음 다섯 계층을 격리해야 합니다.
- 기존 디렉터리를 정리하는 대신 독립된 클론을 사용합니다.
- 작업별 임시
HOME과TMPDIR을 생성합니다. - DerivedData에 작업 전용 디렉터리를 지정합니다.
- 기본 Xcode 버전이 바뀌지 않도록
DEVELOPER_DIR을 고정합니다. - 허용 목록에 포함된 환경 변수만 빌드 프로세스에 전달합니다.
클린 체크아웃 검증의 목적은 빌드를 “한 번 더 정리”하는 것이 아니라, 버전 관리 저장소나 의존성 목록, 파이프라인 매개변수에 포함되지 않은 상태가 암묵적으로 사용되지 않는다는 사실을 증명하는 데 있습니다.
처음 조사할 때는 비교 기준을 하나 유지해야 합니다. 기존 작업 공간에서는 빌드가 성공하고 격리된 작업 공간에서는 실패하는 상태가 필요합니다. 두 빌드에서 동일한 커밋, Scheme, Configuration, Destination을 사용해야 실패 차이에 진단 가치가 생깁니다.
일회성 격리 작업 공간 만들기
다음 스크립트는 기존 저장소로부터 로컬 하드 링크를 사용하지 않는 클론을 만들고 지정한 커밋을 체크아웃한 다음, 사용자 디렉터리와 임시 디렉터리, 빌드 캐시를 모두 일회성 디렉터리에 배치합니다. 프로젝트 이름과 Scheme은 매개변수로 전달하여 환경 차이가 스크립트에 하드코딩되지 않도록 합니다.
#!/bin/bash
set -euo pipefail
SOURCE_REPO="${1:?source repository required}"
REVISION="${2:?revision required}"
PROJECT="${3:?project path required}"
SCHEME="${4:?scheme required}"
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/clean-checkout.XXXXXX")"
trap 'rm -rf "$ROOT"' EXIT
git clone --no-local --no-checkout "$SOURCE_REPO" "$ROOT/repo"
git -C "$ROOT/repo" checkout --detach "$REVISION"
git -C "$ROOT/repo" submodule update --init --recursive
mkdir -p "$ROOT/home" "$ROOT/tmp" "$ROOT/DerivedData"
export HOME="$ROOT/home"
export TMPDIR="$ROOT/tmp"
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
cd "$ROOT/repo"
xcodebuild \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration Debug \
-derivedDataPath "$ROOT/DerivedData" \
CODE_SIGNING_ALLOWED=NO \
build | tee "$ROOT/build.log"
CODE_SIGNING_ALLOWED=NO는 서명이 필요 없는 컴파일 단계를 검증할 때만 적합합니다. 대상에서 아카이브나 서명을 완료해야 한다면 CI가 필요한 자료를 안전하게 주입해야 하며, 스크립트를 통과시키기 위해 개인 사용자 디렉터리의 파일을 참조해서는 안 됩니다.
누락된 의존성을 조기에 드러내기
클론이 완료되면 먼저 git status --porcelain을 실행하며, 결과는 비어 있어야 합니다. 그런 다음 서브모듈 상태와 대용량 파일 객체가 완전한지 확인합니다. 프로젝트에 코드 생성 단계가 있다면 두 종류의 산출물을 명확히 구분해야 합니다. 생성기 소스와 버전은 반드시 고정해야 하며, 생성 결과는 팀 규칙에 따라 커밋하거나 빌드 과정에서 일관되게 생성해야 합니다.
~/bin, /usr/local/bin 또는 특정 팀원의 데스크톱 디렉터리를 임시 해결 경로로 추가하지 마십시오. 보조 도구는 저장소의 도구 디렉터리에 포함하거나, 버전 제약이 적용되는 패키지 관리 목록에 선언하거나, 노드 초기화 과정에서 고정된 버전으로 설치해야 합니다.
실패 로그에서 숨은 입력 찾기
격리 빌드가 실패하면 로그 마지막의 요약이 아니라 실제로 발생한 첫 번째 오류부터 확인합니다. 흔한 신호는 입력 유형에 따라 다음과 같이 분류할 수 있습니다.
| 실패 신호 | 일반적인 원인 | 수정 방향 |
|---|---|---|
No such file or directory |
커밋되지 않은 파일 또는 절대 경로 | 파일을 저장소에 포함하고 저장소 상대 경로 사용 |
command not found |
대화형 Shell 또는 전역 도구에 의존 | 도구 버전을 선언하고 PATH를 명시적으로 설정 |
| 설정값이 비어 있음 | 매개변수가 로컬 설정에만 존재 | CI에서 주입하고 누락 시 즉시 실패 처리 |
| 이전 디렉터리에서만 모듈이 표시됨 | 공유 캐시가 누락된 의존성을 가림 | 기존 캐시를 복사하지 말고 의존성 선언 수정 |
| 스크립트 권한 오류 | 실행 권한 비트가 커밋에 포함되지 않음 | 파일 모드를 수정하고 다시 커밋 |
다음 명령으로 현재 커밋에 대상 파일과 해당 권한이 실제로 포함되어 있는지 확인할 수 있습니다.
git ls-tree -r HEAD -- path/to/file
git status --short
git diff --summary HEAD
오류 경로가 /Users/某个名字/를 가리킨다면 일반적으로 프로젝트 설정, 스크립트 또는 생성 파일에 절대 경로가 기록되어 있다는 뜻입니다. 새 노드에 같은 이름의 디렉터리를 만드는 대신, 해당 경로를 생성한 원본 설정으로 돌아가 수정해야 합니다.
잘못된 실패를 만들지 않으면서 환경 최소화하기
env -i를 직접 사용하면 환경 의존성을 빠르게 찾을 수 있지만, 빌드에 필요한 PATH, 로케일 설정, 임시 디렉터리까지 제거할 수 있습니다. 더 안정적인 방법은 현재 빌드가 실제로 읽는 변수를 먼저 기록한 다음 허용 목록을 만드는 것입니다.
HOME, TMPDIR, PATH, DEVELOPER_DIR, LANG과 파이프라인에서 명시적으로 주입하는 비즈니스 매개변수는 유지하는 것이 좋습니다. 각 사용자 정의 변수에는 다음과 같이 필수값 검사를 적용합니다.
: "${API_BASE_URL:?API_BASE_URL must be provided by CI}"
: "${BUILD_FLAVOR:?BUILD_FLAVOR must be provided by CI}"
이렇게 하면 설정이 누락되었을 때 컴파일 후반부에서 모호한 오류가 발생하는 대신 스크립트 진입 시점에 즉시 실패합니다. 민감한 값은 존재 여부만 확인하고 로그에 출력하지 마십시오. 비대화형 작업에서는 해당 파일이 전혀 로드되지 않을 수 있으므로 빌드 스크립트가 대화형 Shell 설정을 읽어서도 안 됩니다.
일상적인 파이프라인에 검증 단계 추가하기
완전히 독립된 클론은 디스크 사용량과 체크아웃 시간을 늘리므로 모든 소규모 커밋을 차단할 필요는 없습니다. 검증을 두 단계로 나눌 수 있습니다. 일반 작업은 재사용 작업 공간에서 빠른 피드백을 제공하고, 클린 체크아웃 작업은 주요 브랜치 병합 시점, 의존성 또는 Xcode 변경 시점, 릴리스 전에 실행합니다.
검증을 통과하려면 다음 조건을 모두 충족해야 합니다.
- 지정한 커밋을 독립된 클론에 완전하게 체크아웃할 수 있습니다.
- 빌드 전후 작업 트리에 의도하지 않은 변경 사항이 없습니다.
- 빌드가 기존 작업 공간이나 개인 사용자 디렉터리를 읽지 않습니다.
- 모든 보조 도구에 추적 가능한 버전 출처가 있습니다.
- DerivedData를 비운 후에도 예상한 산출물을 생성할 수 있습니다.
- 실패 로그와 환경 요약으로 문제를 재현할 수 있으며 민감한 값은 포함하지 않습니다.
클린 체크아웃은 실패하지만 일반 빌드는 성공한다면 이를 의존성 선언의 결함으로 간주해야 합니다. 캐시 복구를 논의하기 전에 입력 경계부터 수정하십시오. 이 검증 단계를 지속적으로 유지하면 새 노드로의 이전, 병렬 확장, 툴체인 업그레이드를 “다른 머신에서 운에 맡겨 보기”가 아닌 재현 가능한 엔지니어링 프로세스로 전환할 수 있습니다.
자주 묻는 질문
git clean만 실행하면 충분하지 않은 이유는 무엇인가요?
git clean은 현재 작업 트리의 추적되지 않은 파일만 제거합니다. 사용자 HOME 설정, 전역 도구, 공유 캐시, Keychain과 저장소 밖의 절대 경로는 격리하지 못합니다.
클린 체크아웃 빌드가 실패하면 무엇부터 확인해야 하나요?
실패한 명령의 입력 경로와 환경 변수를 먼저 비교하고, 서브모듈, 대용량 파일, 생성 코드, 설정 파일과 스크립트 도구가 저장소 또는 잠금 파일에 선언됐는지 확인합니다.
모든 커밋에서 이 검증을 실행해야 하나요?
필수는 아닙니다. 빠른 검사는 일상 빌드에 두고, 완전한 격리 검증은 기본 브랜치 병합, 도구 체인 변경, 의존성 갱신과 배포 전에 실행하는 방식이 효율적입니다.
다음 빌드 또는 추론 작업을 위한 클라우드 Mac 구성
세 가지 Apple Silicon 구성, 여섯 개 노드와 고정 대여 기간 중에서 선택할 수 있으며, 실제 사용 가능 여부는 콘솔에서 실시간으로 확인됩니다.