증상: Xcode 26 build database locked 오류가 발생했다면 전체 캐시를 먼저 지우지 마세요.
가장 빠른 해법: 공유 빌드 경로의 동시 작업을 멈추고 각 작업에 독립된 DerivedData 경로를 지정한 뒤, 잔류 xcodebuild가 없을 때만 해당 작업의 빌드 데이터베이스를 정리합니다.
이 글은 SSH로 원격 맥에서 xcodebuild를 실행하는 독립 개발자에게 적합합니다. 여러 작업이 같은 프로젝트를 동시에 빌드하는 소규모 팀, 한 대의 맥에서 Build·Test·Archive를 함께 운영하는 담당자도 대상입니다.
오류를 재현한 기록부터 고정하기
build database locked는 그 문장 하나만으로 원인을 확정할 수 없습니다. 같은 데이터베이스에 여러 프로세스가 접근했는지, 비정상 종료 뒤 잠금 상태가 남았는지, 스크립트가 내부에서 또 다른 빌드를 시작했는지 먼저 나눠야 합니다.
비식별화한 실패 기록은 다음 순서로 보존합니다.
- 작업 A가
/srv/runner/project-a에서xcodebuild를 시작합니다. - SSH 연결이 끊겼지만 원격 프로세스가 계속 실행됩니다.
- 작업 A가 끝났는지 확인하지 않고 작업 B가 같은 프로젝트를 다시 시작합니다.
- 두 작업이 같은
DerivedData와 빌드 출력 경로를 사용합니다. - 첫 번째 유효 오류로
build database locked가 출력됩니다.
마지막 오류 줄만 복사하지 말고 다음 항목을 함께 저장해야 합니다.
- 실행한 전체 명령과 작업 디렉터리
- 프로젝트 또는 워크스페이스, Scheme, 구성 이름
-derivedDataPath,OBJROOT,SYMROOT, 아카이브 경로- Build, Test, Archive를 시작한 부모 프로세스
- 오류가 처음 발생한 로그와
xcresult - 프로젝트 이름, 사용자 이름, 호스트 주소, 번들 식별자, 팀 식별자
예를 들어 단일 작업은 다음처럼 경로를 명시할 수 있습니다.
cd /srv/runner/project-a && \
xcodebuild \
-workspace SampleApp.xcworkspace \
-scheme SampleApp \
-configuration Release \
-destination 'generic/platform=iOS' \
-derivedDataPath /srv/runner/jobs/job-184/DerivedData \
-resultBundlePath /srv/runner/jobs/job-184/Test.xcresult \
archive \
-archivePath /srv/runner/jobs/job-184/SampleApp.xcarchive
xcodebuild의 빌드 설정과 경로 해석은 Apple의 빌드 설정 참고 문서와 실제 로그를 함께 대조해야 합니다. 화면에서 보이는 작업 폴더와 명령줄에서 해석되는 경로가 다를 수 있기 때문입니다.
프로세스 충돌과 공유 경로
동시에 실행 중인 빌드 확인
먼저 모든 Xcode 관련 프로세스를 무차별 종료하지 말고, PID와 부모 관계를 확인합니다.
ps -axo pid,ppid,user,etime,command | \
grep -E 'xcodebuild|Xcode|xcbuild|simctl' | grep -v grep
확인할 대상은 다음과 같습니다.
- 같은 Scheme을 빌드하는 작업이 둘 이상인지
- 한 작업이 다른
xcodebuild를 자식 프로세스로 만들었는지 - SSH가 끊긴 뒤에도 이전 작업의 경과 시간이 계속 증가하는지
- 서로 다른 사용자가 같은 출력 디렉터리를 쓰는지
- 재시도 작업이 이전 작업의 취소를 기다리지 않고 시작됐는지
보존할 작업을 정할 때는 가장 최근 작업이라는 이유만으로 선택하지 않습니다. 이미 테스트 결과가 생성되었는지, 아카이브가 완성되었는지, 업로드가 시작되었는지 확인합니다. 실행 중인 작업을 중단하면 테스트 결과, xcarchive, 업로드 상태, 로그가 불완전해질 수 있습니다.
가능하면 정상 종료를 먼저 기다립니다. 그다음 취소해야 하는 작업의 PID를 특정하고, 종료 후 다시 프로세스 목록을 확인합니다. 종료 대상이 명확하지 않다면 강제 종료보다 CI 작업 취소, 부모 작업 종료, 개별 자식 작업 확인 순서가 안전합니다. 테스트 결과 해석과 보존 방식은 Apple의 테스트 실행 문서도 함께 확인합니다.
각 작업의 경로 분리
두 개의 xcodebuild 작업이 같은 DerivedData를 공유하는 구성은 피해야 합니다. 두 작업이 서로 다른 Scheme을 사용해도 공통 중간 산출물과 빌드 데이터베이스에 동시에 접근할 수 있습니다.
CI에서는 작업 식별자를 경로에 포함합니다.
JOB_ROOT="/srv/runner/jobs/${CI_JOB_ID}"
DERIVED_DATA="${JOB_ROOT}/DerivedData"
RESULT_BUNDLE="${JOB_ROOT}/result.xcresult"
ARCHIVE_PATH="${JOB_ROOT}/archive/SampleApp.xcarchive"
xcodebuild \
-workspace SampleApp.xcworkspace \
-scheme SampleApp \
-derivedDataPath "$DERIVED_DATA" \
-resultBundlePath "$RESULT_BUNDLE" \
archive \
-archivePath "$ARCHIVE_PATH"
CI_JOB_ID가 없다면 실행 시각이나 난수보다 CI가 재시도와 결과를 추적할 수 있는 고유 작업 번호를 사용하는 편이 낫습니다. 각 작업은 중간 산출물, xcresult, xcarchive를 별도로 만들고, 최종 산출물만 보관 단계에서 중앙 저장소로 복사합니다.
Apple은 빌드 시스템이 대상 의존성과 스킴 설정을 바탕으로 작업을 구성한다고 설명합니다. Apple의 빌드 시스템 문서와 스킴 사용자화 문서를 기준으로 GUI 설정과 CI 명령의 차이를 대조해야 합니다.
통과 기준은 명확합니다.
- 두 작업이 서로 다른
DerivedData경로를 사용합니다. - 각 작업이 독립적인
xcresult또는 아카이브를 만듭니다. - 한 작업의 취소가 다른 작업의 로그와 산출물을 삭제하지 않습니다.
- 두 작업의 전체 로그에 같은 데이터베이스 잠금 오류가 다시 나타나지 않습니다.
숨은 중첩 빌드와 SSH 잔류 상태
스크립트가 또 다른 빌드를 시작하는 경우
Run Script 단계, 패키지 빌드 도구, 배포 스크립트가 내부에서 xcodebuild를 다시 호출하면 부모 작업과 자식 작업이 같은 경로를 사용할 수 있습니다. 다음 명령으로 저장소 안의 호출 지점을 찾습니다.
grep -R "xcodebuild" . \
--exclude-dir=.git \
--exclude='*.log'
이때 단순히 병렬 빌드를 끄는 것을 기본 해결책으로 삼으면 안 됩니다. 먼저 다음을 확인합니다.
- 내부 호출이 정말 필요한지
- 부모 작업과 자식 작업의 책임이 어디에서 나뉘는지
- 자식 작업의 출력이 어느 디렉터리에 저장되는지
- 입력과 출력이 Xcode 스크립트 단계에 선언되어 있는지
- 실패 시 어떤 작업이 재시도되고 어떤 산출물이 보존되는지
스크립트 수정 전에는 기존 명령을 별도 파일로 보관합니다. 수정 후에는 빌드 로그에서 xcodebuild 시작 횟수와 호출 경로를 비교합니다. Apple의 빌드 중 사용자 스크립트 실행 문서는 스크립트의 입력과 출력 선언을 점검할 때 기준이 됩니다.
SSH 단절 뒤 남은 프로세스
SSH 세션이 끊겼다는 사실은 원격 작업이 종료됐다는 뜻이 아닙니다. ps 결과에서 이전 작업이 남아 있으면 새 작업을 시작하기 전에 다음 순서로 처리합니다.
- PID, 부모 PID, 사용자, 실행 시간, 전체 명령을 파일로 저장합니다.
- 현재 작업이 테스트 중인지, 아카이브 중인지, 업로드 중인지 확인합니다.
- 보존할 작업을 정하고 나머지 작업만 정상 종료를 시도합니다.
- 일정 시간 뒤 같은 PID가 남아 있는지 다시 확인합니다.
- 종료된 작업의 로그와 임시 산출물을 별도 위치에 보관합니다.
- 새 작업에 새로운
DerivedData와 결과 경로를 지정합니다.
SSH 세션이 다시 연결된 뒤에도 이전 작업의 xcarchive가 완성되었다고 가정하면 안 됩니다. 아카이브 디렉터리, Info.plist, 서명 결과, dSYM, xcresult를 각각 확인해야 합니다. 배포용 서명 흐름은 Apple의 배포 서명 문서와 프로젝트의 실제 플랫폼 설정을 함께 검토합니다.
잔류 데이터베이스 정리와 복구 검증
관련 xcodebuild, 테스트, 인덱싱 작업이 모두 종료된 뒤에도 같은 작업에서 오류가 반복될 때만 정리를 시작합니다. 삭제 범위는 다음처럼 좁은 범위에서 넓은 범위로 늘립니다.
- 해당 작업의 임시 디렉터리
- 해당 프로젝트와 작업에 속한
DerivedData - 손상된 중간 빌드 출력
- 새 작업 디렉터리에서 다시 생성한 워크스페이스
소스 코드, 인증서와 프로비저닝 자산, 기존 xcarchive, dSYM, xcresult, 원본 로그는 삭제 대상과 분리해 둡니다. DerivedData를 지워도 이미 완성된 아카이브와 서명 자산을 직접 삭제하는 것은 아니지만, 다시 빌드할 중간 산출물은 사라집니다. 따라서 기존 아카이브를 먼저 보존하고 동일 명령으로 전후 결과를 비교해야 합니다.
원격 맥에서 지속적인 빌드를 운영할 때 필요한 SSH 방식을 검토하면 세션 단절과 작업 유지 방식을 별도로 설계할 수 있습니다. 여러 작업을 처리한다면 CI용 맥 환경 구성 방향도 함께 확인하는 편이 좋습니다.
복구 순서는 다음과 같이 고정합니다.
- 단일 작업으로 Build를 실행합니다.
- 같은 작업에서 Test를 실행하고
xcresult를 확인합니다. - 독립 경로를 사용하는 병렬 Test를 실행합니다.
- 실제 서명 조건으로 Archive를 한 번 실행합니다.
- SSH 재연결 뒤 로그와 프로세스 상태를 확인합니다.
- 작업 취소 후 재시작을 시험합니다.
- 호스트 재시작 뒤 경로 재생성과 산출물 보존을 확인합니다.
Apple은 지속적 통합 환경에서 패키지와 앱을 빌드하는 별도 지침도 제공합니다. CI 빌드 관련 Apple 문서의 원칙과 현재 프로젝트의 실행 로그가 일치하는지 확인합니다.
| 운영 조건 | 우선 조치 | 통과 기준 | 다음 선택 |
|---|---|---|---|
| 단일 작업에서도 오류 발생 | 잔류 프로세스 확인 후 해당 작업 경로만 정리 | 같은 명령이 단일 Build를 통과 | Test와 Archive로 진행 |
두 작업이 같은 DerivedData 사용 |
-derivedDataPath를 작업별로 분리 |
각 작업이 독립 결과를 생성 | 병렬 Test 재개 |
| SSH 단절 뒤 이전 PID가 남음 | 작업 상태와 산출물 확인 후 개별 종료 | 이전 작업이 새 작업과 겹치지 않음 | 재연결 복구 시험 |
스크립트가 xcodebuild를 재호출 |
호출 경계와 출력 소유권 수정 | 로그에 불필요한 중첩 빌드가 없음 | Archive 검증 |
| 단일 Build는 성공하지만 Archive 실패 | 서명 자산, 아카이브 경로, 로그 확인 | 완전한 xcarchive와 dSYM 보존 |
업로드 전 검증 |
| 재시작 뒤에도 반복 발생 | 전용 작업 디렉터리와 프로세스 정책 재설계 | 재시작 후 자동 복구와 로그 보존 | 전용 원격 맥 검토 |
현재 환경에서 작업 디렉터리 분리, 전체 프로세스 제어, 안정적인 상시 실행을 제공하지 못한다면 공유 맥을 계속 고치는 것보다 운영 방식을 바꾸는 편이 낫습니다. 로컬 윈도우나 리눅스 환경은 Xcode 실행이 어렵고, 일반 클라우드 작업은 macOS 전용 도구와 서명 자산을 별도로 연결해야 하며, 공유된 한 대의 맥은 동시 작업 때 경로 충돌과 SSH 잔류 프로세스가 반복될 수 있습니다. 먼저 기존 호스트에서 병렬 작업, SSH 재연결, 재시작 복구를 모두 시험한 뒤에도 격리가 되지 않는다면 KVMFLUX의 원격 맥 이용 방식을 비교해 보세요. 임시 빌드나 검증 환경에는 별도 원격 맥이 더 관리하기 쉬울 수 있지만, 장기간 고정 부하나 물리 장비 접근이 필요한 경우에는 직접 장비를 운영하는 편이 적합합니다.
더 읽어보기
안정적인 원격 빌드 환경을 KVMFLUX에서 시작하세요
KVMFLUX는 엑스코드 개발과 지속적 통합 작업에 활용할 수 있는 원격 맥을 제공합니다. 전용 자원으로 여러 빌드 작업 간 충돌을 줄이고 더욱 안정적인 작업 환경을 구성할 수 있습니다. 원격으로 맥에 접속해 빌드 상태를 확인하고 중단된 작업을 편리하게 이어갈 수 있습니다. 프로젝트 규모와 사용 시간에 맞는 요금제를 선택해 유연하게 운영해 보세요.