원격으로 대규모 Xcode 워크스페이스를 열었을 때 팬 소리는 들리지 않더라도 터미널의 CPU 사용량 그래프가 계속 치솟을 수 있습니다. 코드 완성은 “인덱싱 중”에서 멈추고, 정의로 이동해도 아무 결과가 나오지 않습니다. 이때 가장 위험한 대응은 기다리는 것이 아니라 개발 디렉터리 전체를 곧바로 삭제하는 것입니다. 올바른 순서는 먼저 부하가 빌드에서 발생하는지 SourceKit에서 발생하는지 확인하고, 진단 자료를 보존한 다음, 마지막으로 영향을 받은 인덱스만 다시 만드는 것입니다.
정상적인 재인덱싱과 실제 멈춤부터 구분하기
SourceKit은 프로젝트를 처음 열거나, Xcode 버전을 변경하거나, 의존성 잠금 파일을 수정하거나, 변경 사항이 많은 브랜치로 전환한 뒤 인덱스를 다시 생성합니다. CPU 사용량이 잠시 높다고 해서 반드시 장애인 것은 아닙니다. 먼저 진행 중인 빌드를 중지하고 워크스페이스 파일을 변경하지 않은 상태에서 프로세스 스냅샷을 두세 차례 연속으로 확인합니다.
date
ps -axo pid,ppid,%cpu,%mem,etime,command | grep -E 'SourceKit|sourcekitd|XCBBuildService|swift-frontend' | grep -v grep
다음 세 가지를 중점적으로 확인합니다. XCBBuildService 또는 swift-frontend가 여전히 컴파일 중인지, SourceKit 프로세스의 실행 시간이 계속 늘어나는지, 편집을 멈춘 뒤에도 CPU 부하가 오랫동안 같은 프로세스 그룹에 집중되는지입니다. 빌드 프로세스가 계속 작업 중이라면 먼저 빌드가 끝날 때까지 기다려야 하며, 컴파일 부하를 인덱싱 멈춤으로 오인해서는 안 됩니다.
CPU 수치 한 번만으로 멈춤 여부를 판단할 수는 없습니다. 파일 집합이 안정된 상태이고, 빌드 작업이 없으며, 코드 완성이 오랫동안 진전되지 않고, 로그가 같은 모듈을 반복해서 가리키는 네 가지 조건이 동시에 나타날 때만 정리를 고려할 만합니다.
비교 가능한 진단 자료 보존하기
먼저 Xcode를 재시작하지 마십시오. 프로세스 목록, 로그, 호출 스택을 한곳에 저장할 진단 디렉터리를 만들어야 이후에 복구 조치가 효과가 있었는지 판단할 수 있습니다.
STAMP="$(date +%Y%m%d-%H%M%S)"
OUT="$HOME/sourcekit-diagnostics/$STAMP"
mkdir -p "$OUT"
ps -axo pid,ppid,%cpu,%mem,etime,command > "$OUT/processes.txt"
log show --last 10m --style compact --predicate 'process CONTAINS[c] "SourceKit" OR process == "Xcode"' > "$OUT/sourcekit.log"
PID="$(pgrep -n -f 'SourceKit|sourcekitd')"
test -n "$PID" && sample "$PID" 10 -file "$OUT/sourcekit.sample.txt"
샘플링 중에는 브랜치를 전환하거나 파일을 일괄 수정하지 마십시오. 로그를 검토할 때는 반복해서 나타나는 모듈 이름, 읽을 수 없는 경로, 플러그인 로드 실패, 요청 취소가 반복되는 패턴부터 찾습니다. 호출 스택이 같은 Swift 파일이나 같은 생성 파일을 반복해서 파싱하는 위치에 머문다면, 먼저 스크립트가 해당 파일을 계속 다시 쓰고 있는지 확인합니다.
생성 파일로 인한 반복 처리 배제하기
흔한 원인 중 하나는 코드 생성기가 실행될 때마다 타임스탬프를 다시 쓰는 것입니다. 내용이 달라지지 않았더라도 인덱서는 파일을 다시 처리하게 됩니다. 생성 스크립트는 먼저 내용을 비교한 뒤 원자적 교체 방식으로 대상 파일을 기록해야 합니다. 또한 생성 디렉터리가 폴더 참조와 소스 디렉터리라는 두 가지 방식으로 프로젝트에 동시에 추가되어 있지 않은지도 확인해야 합니다.
워크스페이스별로 DerivedData 분리하기
하나의 DerivedData를 공유하면 서로 다른 브랜치와 Xcode 버전, 병렬 작업이 서로의 데이터를 오염시킬 수 있습니다. 클라우드 Mac에서는 각 워크스페이스 또는 CI 작업에 명확한 전용 경로를 지정하는 편이 더 안정적입니다.
ROOT="$PWD"
DERIVED="$ROOT/.derived-data"
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-derivedDataPath "$DERIVED" \
-showBuildSettings > "$ROOT/build-settings.txt"
.derived-data는 저장소에 커밋하면 안 됩니다. 병렬 작업에는 .derived-data/job-42처럼 작업 식별자도 추가하여 두 빌드가 모듈 캐시에 동시에 쓰지 않도록 해야 합니다.
| 시나리오 | DerivedData 전략 | 인덱싱 전략 |
|---|---|---|
| 원격 대화형 개발 | 워크스페이스별 고정 독립 디렉터리 | 활성화 유지 |
| 일회성 검증 빌드 | 작업별 독립 디렉터리 | 필요에 따라 활성화 |
| 빌드와 테스트만 수행하는 CI | 작업별 독립 디렉터리 | 인덱스 쓰기 비활성화 가능 |
| Xcode 버전 전환 | 새 버전 디렉터리 사용 | 다시 생성 |
코드 완성이 필요하지 않은 CI 작업에는 명령줄에 COMPILER_INDEX_STORE_ENABLE=NO를 추가할 수 있습니다. 이를 공유 프로젝트 설정에 무조건 적용하면 개발자가 정의로 이동, 진단, 코드 완성 기능을 사용할 수 없게 되므로 주의해야 합니다.
영향 범위에 따라 단계적으로 복구하기
정리를 시작하기 전에 워크스페이스를 닫고 xcodebuild, swift-frontend 또는 SourceKit 프로세스가 대상 디렉터리를 계속 사용하고 있지 않은지 확인합니다. 첫 단계에서는 인덱스와 모듈 캐시만 제거합니다.
DERIVED="$PWD/.derived-data"
rm -rf "$DERIVED/Index.noindex"
rm -rf "$DERIVED/ModuleCache.noindex"
프로젝트를 다시 연 뒤 브랜치와 의존성을 그대로 유지하고 첫 인덱싱이 끝날 때까지 기다립니다. 문제가 다시 발생하면 ~/Library/Developer 전체를 비우지 말고 현재 프로젝트의 독립 DerivedData만 삭제합니다. 그래도 문제가 재현된다면 진단 자료로 돌아가 특정 브랜치에서만 발생하는지, 특정 생성 파일이 계속 변경되는지, 특정 컴파일러 플러그인이 현재 Xcode와 호환되지 않는지 확인해야 합니다.
“컴퓨터를 재시작하니 정상으로 돌아왔다”는 사실을 결론으로 삼아서는 안 됩니다. 재시작은 프로세스를 종료할 뿐이며, 캐시 손상인지, 입력 파일의 반복 변경인지, 툴체인 차이인지 설명해 주지 못합니다.
재현 가능한 검증 체크리스트 만들기
복구 후에는 동일한 커밋, 동일한 Xcode 경로, 동일한 DerivedData 전략으로 다시 검증합니다. 다음 결과를 기록하는 것이 좋습니다.
xcodebuild -version과xcode-select -p가 예상한 툴체인을 가리킵니다.- 워크스페이스를 연 뒤 인덱싱이 완료되고 코드 완성과 정의로 이동 기능이 복구됩니다.
- 편집과 빌드를 중지하면 SourceKit CPU 사용량이 내려갑니다.
- 로그에 같은 파일이나 모듈이 반복해서 나타나지 않습니다.
- 워크스페이스를 연속으로 닫았다가 다시 열어도 문제가 재현되지 않습니다.
- CI와 대화형 개발에서 서로 다른 DerivedData 디렉터리를 사용합니다.
NowMini의 원격 세션에서도 진단 디렉터리를 빌드 로그와 함께 보관해야 합니다. 단, 소스 코드 본문이나 키, 마스킹하지 않은 환경 변수는 수집하지 마십시오. 최종 목표는 CPU 사용량을 즉시 0으로 만드는 것이 아니라 인덱싱 입력이 안정적이고, 프로세스가 계속 진행되며, 정리 작업의 영향이 현재 프로젝트에만 한정된다는 사실을 입증하는 것입니다.
자주 묻는 질문
SourceKit의 CPU 사용량이 높으면 항상 인덱싱이 멈춘 것인가요?
아닙니다. 대형 프로젝트를 처음 열거나 Xcode 버전과 의존성이 바뀌면 높은 사용량이 정상일 수 있습니다. 같은 모듈 처리가 오래 반복되고 코드 완성이 진행되지 않을 때 정체로 판단합니다.
DerivedData 전체를 삭제해야 하나요?
대부분 필요하지 않습니다. Xcode와 빌드를 중지한 뒤 프로젝트 전용 경로의 Index.noindex와 ModuleCache.noindex만 삭제하십시오. 문제가 반복될 때만 해당 프로젝트 경로를 다시 만듭니다.
CI 빌드에서 인덱스 생성을 끌 수 있나요?
가능합니다. 비대화형 빌드와 테스트 작업에는 COMPILER_INDEX_STORE_ENABLE=NO를 설정할 수 있습니다. 개발 세션에는 탐색과 코드 완성을 위해 인덱스를 유지해야 합니다.
전용 물리 노드에서 다음 작업 실행
M4, 16GB RAM, 256GB SSD를 탑재한 클라우드 Mac을 사용하고, 작업 주기에 맞춰 싱가포르, 도쿄, 서울 또는 홍콩 노드를 선택하세요.