같은 저장소가 개발용 Mac에서는 정상적으로 작동하지만 원격 빌드 작업에서는 Module not found 오류가 발생하거나, 패키징 후 일부 리소스가 누락되는 경우가 있습니다. 이럴 때 캐시부터 반복해서 삭제할 필요는 없습니다. macOS에서 일반적으로 사용하는 기본 파일 시스템은 파일 이름의 대소문자를 구분하지 않으므로 Config.json과 config.json을 같은 경로로 처리할 수 있습니다. 스크립트나 의존성, 배포 대상이 경로의 대소문자를 엄격하게 구분하는 환경에 들어가야 비로소 숨어 있던 문제가 드러납니다. 가장 안전한 점검 방법은 시스템 디스크를 변경하는 것이 아니라, 클라우드 Mac에 격리된 대소문자 구분 APFS 볼륨을 마운트하는 것입니다.
먼저 경로 대소문자 문제인지 확인하기
실패 로그에서는 마지막 종료 코드만 보지 말고 경로부터 확인해야 합니다. 다음과 같은 현상이 있다면 우선적으로 점검하십시오.
| 현상 | 가능한 원인 | 확인 방법 |
|---|---|---|
| 로컬에서는 import되지만 빌드 작업에서 모듈을 찾지 못함 | import 경로와 실제 파일 이름이 다름 | git ls-files로 전체 경로 확인 |
| 리소스 두 개를 커밋했지만 하나만 남음 | 이름이 대소문자만 다름 | 대소문자 구분 볼륨에서 다시 클론 |
| 정리 후 첫 빌드가 실패함 | 기존 산출물이 잘못된 참조를 가리고 있었음 | 출력 디렉터리를 삭제한 뒤 전체 빌드 |
| 패키징 단계에서 파일이 덮어써짐 | 스크립트가 이름을 일괄 변환함 | 복사 및 이름 변경 단계 점검 |
동시에 diskutil info /를 실행해 현재 시스템 볼륨의 File System Personality 값을 기록합니다. 이 단계는 환경을 확인하기 위한 용도일 뿐이므로 실행 중인 시스템 볼륨을 직접 변환해서는 안 됩니다.
경로 문제는 반드시 깨끗한 작업 공간에서 재현해야 합니다. 기존 캐시를 테스트 볼륨으로 복사하면 원래 문제가 다시 가려질 수 있습니다.
격리된 APFS 테스트 볼륨 만들기
희소 이미지는 실제로 기록된 데이터만큼 확장되므로 임시 점검에 적합합니다. 아래에서는 최대 80GB인 테스트 볼륨을 만듭니다. 프로젝트에 대용량 의존성이 포함되어 있다면 저장소, 의존성, 빌드 산출물의 최대 사용량을 고려해 공간을 확보해야 합니다.
mkdir -p "$HOME/apfs-lab"
hdiutil create \
-size 80g \
-type SPARSEBUNDLE \
-fs "Case-sensitive APFS" \
-volname BuildCase \
"$HOME/apfs-lab/BuildCase.sparsebundle"
hdiutil attach "$HOME/apfs-lab/BuildCase.sparsebundle"
diskutil info "/Volumes/BuildCase"
출력에 대소문자 구분이 명확히 표시되는지 확인하고 마운트 지점이 /Volumes/BuildCase인지 검증합니다. 액세스 키, 서명 자료 또는 장기 보관 데이터는 이 임시 볼륨에 저장하지 마십시오.
새 볼륨에서 저장소를 다시 받기
충돌한 파일이 이미 사라졌을 수 있으므로 기존 작업 공간에서 직접 끌어다 복사하면 안 됩니다. 관리되는 저장소에서 다시 클론하고 별도의 빌드 디렉터리를 만드십시오.
mkdir -p /Volumes/BuildCase/work
cd /Volumes/BuildCase/work
git clone "$REPOSITORY_URL" project
cd project
git status --short
이 시점에는 git status 출력이 없어야 합니다. 클론 단계에서 대상 경로가 이미 존재한다는 오류가 발생한다면 대개 저장소 트리에 대소문자를 무시했을 때 이름이 겹치는 경로가 있다는 뜻입니다.
Git 파일 이름과 스크립트 참조 감사하기
다음 스크립트는 Git이 추적하는 경로만 읽어 Unicode 정규화 형식으로 변환한 뒤, 대소문자를 접어 그룹화합니다. 파일을 수정하지는 않습니다.
from collections import defaultdict
import subprocess
import unicodedata
raw = subprocess.check_output(
["git", "ls-files", "-z"],
text=True
)
groups = defaultdict(list)
for path in raw.split(" "):
if not path:
continue
key = unicodedata.normalize("NFC", path).casefold()
groups[key].append(path)
found = False
for paths in groups.values():
if len(paths) > 1:
found = True
print("COLLISION")
for path in paths:
print(f" {path}")
raise SystemExit(1 if found else 0)
파일을 tools/check_path_case.py로 저장한 뒤 python3 tools/check_path_case.py를 실행합니다. 종료 코드가 1이면 이름을 하나로 통일하고 git mv로 변경 사항을 커밋해야 합니다. 대소문자만 바꿀 때는 기존 작업 공간이 변경을 무시하지 않도록 먼저 임시 이름으로 변경한 다음 최종 이름으로 바꿀 수 있습니다.
git mv Sources/config.json Sources/config.tmp
git mv Sources/config.tmp Sources/Config.json
파일 이름이 충돌하지 않는다고 해서 참조까지 올바른 것은 아닙니다. 빌드 스크립트, 리소스 목록, 프로젝트 설정, 테스트 픽스처에 남은 이전 경로를 계속 검색하십시오. 특히 자동 생성 파일과 수동 작성 설정 사이의 차이를 주의해서 확인해야 합니다.
검사를 재현 가능한 빌드에 연결하기
테스트 작업은 먼저 마운트 지점을 확인하고 경로 감사를 실행한 다음, 빈 디렉터리에서 빌드해야 합니다. 그래야 테스트 볼륨이 마운트되지 않았을 때 파일이 시스템 디스크에 기록되는 일을 막을 수 있습니다.
set -euo pipefail
test -d /Volumes/BuildCase
cd /Volumes/BuildCase/work/project
python3 tools/check_path_case.py
rm -rf .build-output
mkdir .build-output
./scripts/build.sh "$PWD/.build-output"
프로젝트에 통합된 빌드 진입점이 없다면 기존 명령을 먼저 scripts/build.sh로 감싸고 출력 디렉터리를 명시적으로 전달하십시오. 의존성 캐시는 별도로 마운트할 수 있지만 최초 검증에서는 기존 캐시를 비활성화해야 합니다. 검증을 통과한 뒤 하나씩 다시 활성화하면 어느 계층에서 잘못된 경로가 유입되는지 판단할 수 있습니다.
자주 발생하는 실수와 마무리 점검
조사를 마치기 전에 다음 항목을 하나씩 확인하십시오.
- 저장소를 기존 디렉터리에서 복사하지 않고 테스트 볼륨 안에 다시 클론했습니다.
- 파일 이름 수정에
git mv를 사용했으며 커밋에서 이름 변경이 명확히 보입니다. - 스크립트가 경로를 강제로 모두 소문자 또는 대문자로 변환하지 않습니다.
- 리소스 복사 명령은 같은 이름의 대상이 있으면 조용히 덮어쓰지 않고 실패합니다.
- 빌드 산출물, 의존성 디렉터리, 로그가 모두 예상한 마운트 지점에 있습니다.
- 깨끗한 빌드와 증분 빌드를 각각 한 번씩 실행했으며 결과가 일치합니다.
검증이 끝나면 필요한 로그를 먼저 내보낸 다음 테스트 볼륨을 마운트 해제합니다.
hdiutil detach "/Volumes/BuildCase"
데이터를 보관할 필요가 없는지 확인한 뒤에만 BuildCase.sparsebundle을 삭제하십시오. 이 검사를 장기적인 품질 게이트로 운영하려면 테스트 데이터를 보존하는 대신 생성 스크립트를 보관하고, 작업을 시작할 때마다 볼륨 형식, 남은 용량, 마운트 경로를 확인해야 합니다.
자주 묻는 질문
클라우드 Mac의 시스템 볼륨을 대소문자 구분 형식으로 바꿔야 하나요?
아니요. 별도의 APFS 희소 이미지를 마운트하고 저장소와 빌드 디렉터리만 배치하면 시스템 볼륨을 변경하지 않고 검사할 수 있습니다.
Git이 파일명 대소문자 충돌을 바로 알리지 않는 이유는 무엇인가요?
Git은 경로 표기를 기록하지만 대소문자를 구분하지 않는 작업 트리는 서로 다른 이름을 같은 파일로 처리할 수 있습니다. 검사 볼륨에서 새로 복제해야 합니다.
독립형 Mac mini 물리 노드가 필요하신가요?
사용 가능한 구성, 노드 및 고정 결제 주기를 확인하고, 글의 단계를 지속적으로 사용할 수 있는 클라우드 Mac 환경에 적용해 보세요.