엔지니어링 실무

클라우드 Mac에서 Xcode 컴파일 경고 기준선 게이트 구축하기

클라우드 Mac에서 Xcode 컴파일 경고 기준선 게이트 구축하기

여러 해 동안 유지해 온 Xcode 프로젝트에는 컴파일 경고가 수십 개씩 쌓여 있는 경우가 많습니다. 이 상태에서 SWIFT_TREAT_WARNINGS_AS_ERRORS를 바로 활성화하면 기본 브랜치를 즉시 빌드할 수 없게 됩니다. 그렇다고 방치하면 새 경고가 기존 경고의 잡음 속에 섞입니다. 더 현실적인 방법은 클라우드 Mac에 검토를 마친 경고 기준선을 고정하고, CI가 이번 변경에서 새로 추가된 항목만 거부하도록 만드는 것입니다.

먼저 게이트의 범위 정의하기

경고 게이트는 로그에 있는 warning:의 개수를 단순히 세는 방식이 아닙니다. 종속성 다운로드, 빌드 스크립트, 시스템 도구도 같은 문자열을 출력할 수 있습니다. 이를 그대로 집계하면 결과가 불안정할 뿐만 아니라 개발자가 어느 파일을 수정해야 하는지도 알 수 없습니다.

각 경고를 ‘저장소 기준 상대 경로 + 경고 본문’ 형식으로 정규화하고 줄 번호, 열 번호, 임시 디렉터리, 타임스탬프는 제거하는 것이 좋습니다. 이렇게 하면 코드 위치가 위아래로 이동해도 새 서명이 생기지 않으며, 파일 이름이나 경고 내용이 바뀌면 여전히 검토가 필요합니다.

내용 서명 포함 여부 이유
저장소 기준 상대 경로 담당 파일 식별
경고 본문 문제 유형 구분
줄 번호와 열 번호 아니요 코드 수정 후 쉽게 달라짐
DerivedData 경로 아니요 작업마다 달라질 수 있음
외부 종속성 경고 기본적으로 아니요 일반적으로 팀이 직접 수정할 수 없음

기준선은 ‘현재 알려져 있고 수용한 기술 부채’를 의미할 뿐, 해당 경고가 올바르다는 뜻은 아닙니다. 기존 경고를 하나 제거할 때마다 기준선도 함께 줄여야 합니다.

재현 가능한 경고 서명 생성하기

먼저 작업 공간, Scheme, 구성, SDK를 고정합니다. 개발자 환경에서는 Debug를 사용하고 CI에서는 Release를 사용하면 조건부 컴파일 경로가 달라지므로, 생성된 기준선을 비교해도 의미가 없습니다. 아래 스크립트는 호출 측에서 작업 공간과 Scheme을 명시적으로 전달하도록 요구하며 전체 출력을 저장합니다.

#!/bin/bash
set -u

WORKSPACE="${WORKSPACE:?set WORKSPACE}"
SCHEME="${SCHEME:?set SCHEME}"
ROOT="$(git rev-parse --show-toplevel)"
OUT="$ROOT/.ci-artifacts"
DERIVED="$OUT/DerivedData"

mkdir -p "$OUT"
rm -rf "$DERIVED"

set +e
xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration Release \
  -sdk iphoneos \
  -derivedDataPath "$DERIVED" \
  CODE_SIGNING_ALLOWED=NO \
  build 2>&1 | tee "$OUT/xcodebuild.log"
BUILD_STATUS=${PIPESTATUS[0]}
set -e

grep -F "$ROOT/" "$OUT/xcodebuild.log" \
  | grep " warning: " \
  | sed "s#${ROOT}/##" \
  | sed -E 's#:[0-9]+:[0-9]+: warning: # | #' \
  | LC_ALL=C sort -u \
  > "$OUT/warnings.current"

exit "$BUILD_STATUS"

CODE_SIGNING_ALLOWED=NO는 컴파일만 검증하는 작업에 적합합니다. 아카이브하거나 서명을 검증해야 하는 파이프라인에는 그대로 적용하면 안 됩니다. 빌드 실패와 경고 회귀도 별도로 처리해야 합니다. xcodebuild 자체가 실패했다면 빈 경고 파일로 실제 오류를 덮는 대신 해당 종료 코드를 우선 반환해야 합니다.

스크립트 출력과 여러 줄 진단 처리하기

일부 Run Script 단계에서는 소스 코드 좌표가 없는 형식으로 사용자 정의 경고를 출력합니다. 팀에서 해당 스크립트를 관리한다면 고정 접두사를 정하고 별도의 두 번째 파싱 규칙을 만들 수 있습니다. 모든 출력을 하나의 광범위한 정규식으로 수집하지 마십시오. 그러면 네트워크 안내나 도구 업데이트 알림까지 기준선에 포함됩니다.

Swift의 추가 설명 줄에는 일반적으로 warning:이 없으므로 독립적인 서명으로 사용하기에는 적합하지 않지만, 전체 로그에는 보존해야 합니다. 게이트는 빠른 판정을 담당하고 전체 로그는 맥락 복원을 담당하므로, 두 가지는 서로 대체할 수 없습니다.

첫 기준선 검토 후 커밋하기

처음 생성한 warnings.current를 자동화 작업이 바로 기준선으로 복사해서는 안 됩니다. 먼저 경로별로 담당자를 지정하고 중복 항목을 제거한 뒤, 파생 파일이나 종속성 소스 코드, 임시 디렉터리가 포함되지 않았는지 확인합니다. 검토가 끝나면 다음 명령을 실행합니다.

mkdir -p .ci
LC_ALL=C sort -u .ci-artifacts/warnings.current > .ci/xcode-warnings.baseline
git add .ci/xcode-warnings.baseline
git commit -m "Add reviewed Xcode warning baseline"

기준선은 코드와 함께 버전 관리해야 합니다. 기준선을 늘리는 모든 커밋에는 새로 추가된 서명과 그 이유가 표시되어야 합니다. 실패한 작업이 새 경고를 자동으로 ‘학습’하게 해서는 안 됩니다. 그렇게 하면 가장 차단이 필요한 순간에 게이트가 스스로 경고를 허용하게 됩니다.

Xcode를 업그레이드하면 컴파일러의 진단 문구가 달라질 수 있습니다. 올바른 절차는 별도 브랜치에서 전체 빌드를 실행하고, 실제로 새로 발생한 문제와 문구 변경을 구분한 다음 기준선을 한 번에 갱신하면서 도구 체인 버전을 기록하는 것입니다. 비교 스크립트에 끝없이 느슨해지는 퍼지 매칭을 추가하는 방식으로 대응해서는 안 됩니다.

CI에서 새 경고만 차단하기

현재 결과와 기준선을 모두 정렬하면 comm으로 현재 빌드에만 존재하는 서명을 찾을 수 있습니다.

BASELINE=".ci/xcode-warnings.baseline"
CURRENT=".ci-artifacts/warnings.current"
NEW=".ci-artifacts/warnings.new"

test -f "$BASELINE"
LC_ALL=C sort -u "$BASELINE" -o "$BASELINE"
LC_ALL=C sort -u "$CURRENT" -o "$CURRENT"
LC_ALL=C comm -13 "$BASELINE" "$CURRENT" > "$NEW"

if test -s "$NEW"; then
  printf '%s
' "New Xcode warnings detected:"
  cat "$NEW"
  exit 42
fi

xcodebuild.log, warnings.current, warnings.new는 함께 아카이브해야 합니다. 종료 코드 42는 팀 내부 규칙일 뿐이며, 핵심은 ‘컴파일 실패’와 ‘새 경고’를 서로 다른 원인으로 표시하는 것입니다. 개발자가 경로와 본문을 확인할 수 있어야 한 번의 피드백 주기 안에 문제를 수정할 수 있습니다.

여러 작업이 서로 다른 Target을 병렬로 빌드한다면 각각 결과를 생성한 다음 마지막에 병합하고 정렬해 중복을 제거해야 합니다. 여러 프로세스가 같은 파일에 동시에 쓰게 해서는 안 됩니다. 파일 잘림과 쓰기 내용의 뒤섞임으로 인해 간헐적인 오판이 발생할 수 있습니다.

기준선을 영구 동결하지 않고 계속 줄이기

게이트를 도입한 뒤에는 기존 경고를 수정할 때마다 기준선에서 해당 줄을 삭제해야 합니다. 빌드를 차단하지 않는 검사를 추가해 ‘기준선에는 있지만 현재 빌드에서는 사라진’ 항목을 찾고, 커밋 작성자에게 함께 정리하도록 알릴 수도 있습니다. 그러면 기준선은 한 방향으로만 줄어들며, 아무도 이해하지 못하는 과거 목록으로 남지 않습니다.

안정적으로 운영하기 전에는 네 가지 항목을 추가로 확인해야 합니다. CI와 로컬 환경이 같은 Xcode 버전을 사용하는지, Scheme에 예상한 Target이 포함되어 있는지, 로캘 설정이 도구 출력에 영향을 주는지, 저장소 경로 필터가 자체 소스 코드를 모두 포함하는지 점검하십시오. OnceMac 노드의 실행 디렉터리도 작업이 고정된 위치에 직접 생성하도록 해야 이전 빌드의 로그와 DerivedData가 재사용되는 일을 막을 수 있습니다.

기준선이 0이 되면 차이 비교를 제거하고 컴파일러에서 경고를 오류로 처리하는 정책을 활성화할 수 있습니다. 그전까지 기준선 게이트는 기존 부채를 숨기지 않으면서 새 부채가 계속 늘어나는 것도 막아 주는 현실적인 전환 경로입니다.

자주 묻는 질문

처음부터 모든 경고를 오류로 처리하면 안 되나요?

기존 경고가 많은 프로젝트는 모든 빌드가 즉시 실패합니다. 먼저 기준선으로 신규 경고만 막고 기존 항목을 줄인 뒤 엄격한 옵션을 적용하는 편이 안전합니다.

소스 줄 번호가 바뀌면 오탐이 발생하나요?

서명에 줄과 열 번호가 남아 있으면 발생할 수 있습니다. 저장소 기준 상대 경로와 경고 본문만 남기고, Xcode 업그레이드에 따른 문구 변경은 별도로 검토합니다.

의존성에서 발생한 경고도 기준선에 넣어야 하나요?

일반적으로 제외합니다. 팀이 관리하는 저장소 내부 소스만 필터링하고, 책임 범위에 있는 내부 의존성만 명시적인 경로 규칙으로 추가합니다.

전용 물리 Mac mini

다음 빌드를 독립 물리 노드에서 실행하세요.

칩, 메모리, 저장 공간, 노드와 대여 기간을 선택해 다른 고객과 리소스를 공유하지 않는 클라우드 Mac을 이용하세요.

구성 선택 후 대여하기