HexVM 엔지니어링 실무

클라우드 Mac에서 Xcode 빌드 설정 변경 감사하기

클라우드 Mac에서 Xcode 빌드 설정 변경 감사하기

동일한 커밋이 개발용 Mac에서는 정상적으로 아카이브되지만 HexVM 클라우드 Mac에서는 배포 대상이 바뀌거나 특정 아키텍처가 갑자기 빌드에서 빠지는 경우가 있습니다. 이런 문제의 원인은 소스 코드보다 Xcode가 최종적으로 해석한 빌드 설정에 있는 경우가 많습니다. 프로젝트 파일, .xcconfig, 환경 변수, 명령줄 인수가 계층적으로 값을 덮어쓰기 때문에 파일 하나만 검토해서는 Target에 실제로 적용된 설정을 확인하기 어렵습니다.

이러한 설정 드리프트가 곧바로 빌드 실패로 이어지는 것은 아닙니다. 먼저 산출물만 달라졌다가 배포 단계에서야 문제가 드러날 수도 있습니다. 더 안정적인 방법은 노이즈를 제거한 유효 설정을 기준선으로 저장하고, 병합할 때와 정식 아카이브를 만들기 전에 다시 생성해 비교하는 것입니다.

감사할 설정 범위부터 정의하기

xcodebuild의 전체 출력을 그대로 저장하지 마십시오. 절대 경로, 임시 디렉터리, 빌드 번호는 의미 없는 차이를 대량으로 만들기 때문에 설정을 먼저 세 가지 유형으로 나누는 것이 좋습니다.

유형 대표 키 처리 방법
산출물 경계 PRODUCT_BUNDLE_IDENTIFIER, SUPPORTED_PLATFORMS, ARCHS 변경 시 검토
컴파일 동작 SWIFT_VERSION, SWIFT_OPTIMIZATION_LEVEL, GCC_PREPROCESSOR_DEFINITIONS 변경 시 검토
노드별 노이즈 BUILD_DIR, TEMP_DIR, PROJECT_TEMP_DIR 삭제 또는 정규화

코드 서명 관련 설정도 기준선에 포함해야 하지만 인증서 파일, 개인 키 내용, 임시 자격 증명을 저장소에 기록해서는 안 됩니다. 감사 대상은 설정 이름, 서명 방식, 권한 경계이지 민감한 자료의 복사본이 아닙니다.

기준선은 “절대 변경되지 않는” 설정 파일이 아닙니다. 코드 리뷰가 필요한 예상 상태를 기록한 것입니다. 변경은 허용되지만 반드시 확인할 수 있고, 설명할 수 있으며, 되돌릴 수 있어야 합니다.

Target에 실제로 적용된 설정 내보내기

먼저 프로젝트, Scheme, 빌드 구성, 대상 플랫폼을 고정합니다. 워크스페이스 기반 프로젝트라면 -project-workspace로 바꾸고 나머지 인수는 동일하게 유지합니다.

mkdir -p .ci/build-settings

xcodebuild \
  -project App.xcodeproj \
  -scheme App \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -showBuildSettings \
  > .ci/build-settings/raw.txt

실행하기 전에 xcodebuild -list -project App.xcodeproj로 Scheme이 공유되어 있는지 확인합니다. 파이프라인에서 기능 플래그나 사용자 지정 SYMROOT 같은 추가 인수를 사용한다면 기준선을 생성할 때와 실제 빌드를 수행할 때 반드시 같은 인수 집합을 전달해야 합니다. 그렇지 않으면 서로 다른 두 컨텍스트를 비교하게 됩니다.

Target이 여러 개인 프로젝트에서는 이름이 같은 키가 여러 묶음으로 출력됩니다. 이때 단순히 중복을 제거하지 말고 Target 제목을 유지하거나 Scheme별로 파일을 따로 만드십시오. 앱, 확장 프로그램, 테스트 Target은 원래 서로 다른 배포 범위를 가질 수 있습니다.

경로 노이즈를 제거해 안정적인 기준선 만들기

아래 스크립트는 KEY = VALUE 형식의 행을 추출하고 임시 디렉터리 키를 삭제한 다음, 작업 디렉터리와 사용자 홈 디렉터리를 안정적인 표식으로 치환합니다. 이 스크립트는 비밀 키를 해석하지 않으며 전체 환경 변수 목록도 출력하지 않습니다.

from pathlib import Path
import os

source = Path(".ci/build-settings/raw.txt")
target = Path(".ci/build-settings/current.txt")

ignored = {
    "BUILD_DIR",
    "BUILD_ROOT",
    "CONFIGURATION_BUILD_DIR",
    "DERIVED_FILES_DIR",
    "PROJECT_TEMP_DIR",
    "TARGET_TEMP_DIR",
    "TEMP_DIR"
}

root = str(Path.cwd())
home = str(Path.home())
rows = []

for line in source.read_text().splitlines():
    stripped = line.strip()
    if " = " not in stripped:
        continue
    key, value = stripped.split(" = ", 1)
    if key in ignored:
        continue
    value = value.replace(root, "<ROOT>").replace(home, "<HOME>")
    rows.append(f"{key}={value}")

target.write_text("
".join(sorted(rows)) + "
")

첫 결과를 확인한 뒤 current.txtrelease-ios.txt처럼 용도를 나타내는 이름의 기준선 파일로 복사하고 저장소에 커밋합니다.

python3 .ci/normalize_build_settings.py
cp .ci/build-settings/current.txt \
   .ci/build-settings/release-ios.txt

의존성 경로가 계속 자주 바뀐다면 먼저 해당 경로가 컴파일 입력에 영향을 주는지 판단하십시오. “차이 없음” 상태를 만들기 위해 HEADER_SEARCH_PATHS, FRAMEWORK_SEARCH_PATHS, OTHER_SWIFT_FLAGS를 필터링해서는 안 됩니다. 이런 키의 변화가 바로 찾아내야 할 문제인 경우가 많습니다.

차이를 파이프라인 게이트로 전환하기

검사 작업은 의존성 해석이 끝난 뒤, 정식 아카이브를 시작하기 전에 실행해야 합니다. 그래야 전체 검색 경로를 확보하면서도 비용이 큰 아카이브 단계가 시작되기 전에 잘못된 설정을 차단할 수 있습니다.

set -euo pipefail

xcodebuild \
  -project App.xcodeproj \
  -scheme App \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -showBuildSettings \
  > .ci/build-settings/raw.txt

python3 .ci/normalize_build_settings.py

diff -u \
  .ci/build-settings/release-ios.txt \
  .ci/build-settings/current.txt

diff가 0이 아닌 상태를 반환하면 파이프라인을 중단하고 차이 내용을 보존해야 합니다. 새 파일로 기준선을 자동 덮어쓰지 마십시오. 그렇게 하면 게이트가 단순한 기록 도구로 전락합니다.

설정 허용 목록

팀은 무해하다고 확실히 판단한 키에 대해 명시적인 무시 목록을 관리할 수 있습니다. 다만 목록은 짧게 유지하고 각 항목의 이유를 기록해야 합니다. 다음 변경은 계속 차단하는 것이 좋습니다.

  • IPHONEOS_DEPLOYMENT_TARGETSUPPORTED_PLATFORMS
  • ARCHS, EXCLUDED_ARCHSONLY_ACTIVE_ARCH
  • SWIFT_VERSION 및 최적화 수준
  • CODE_SIGN_STYLE 및 서명 ID 선택 방식
  • PRODUCT_BUNDLE_IDENTIFIER 및 권한 파일 경로
  • DEBUG_INFORMATION_FORMAT 및 링커 인수

흔한 오탐과 실제 드리프트 조사하기

실행할 때마다 대량의 경로 차이가 나타난다면 먼저 루트 디렉터리 치환이 심볼릭 링크를 해석한 실제 경로까지 포함하는지 확인하십시오. 작업 시작 시 pwd -P를 기록하고 스크립트가 논리 경로와 실제 경로를 모두 정규화하도록 할 수 있습니다.

로컬에서는 차이가 없지만 클라우드에서 계속 차이가 발생한다면 다음 순서로 확인합니다. 동일한 Scheme을 사용하는지, 구성 이름이 같은지, 명령줄에서 설정이 추가되는지, 의존성 해석이 완료됐는지, 환경 변수가 .xcconfig 확장에 사용되는지 점검하십시오. 결과에 맞추기 위해 기준선부터 수정해서는 안 됩니다.

실제 변경이 발견되면 먼저 출처를 파악한 뒤 기준선을 업데이트합니다.

  1. xcodebuild -showBuildSettings를 사용해 변경이 어느 Target에 속하는지 확인합니다.
  2. 프로젝트 파일, .xcconfig, 파이프라인 인수에서 해당 키를 검색합니다.
  3. 변경 목적과 Debug 및 Release에 미치는 영향을 설명합니다.
  4. 클린 빌드와 아카이브 검증을 한 번 완료합니다.
  5. 설정 변경과 기준선 업데이트를 같은 리뷰에 포함합니다.

지속 가능한 감사 주기 만들기

설정 기준선은 Scheme, 빌드 구성, 플랫폼별로 분리해야 하며 하나의 파일로 모든 시나리오를 처리해서는 안 됩니다. 일반적인 커밋에서는 기본 앱의 Release 기준선만 검사하고, 확장 프로그램, 테스트 번들, 배포 프로세스와 관련된 변경이 있을 때 해당 검사를 추가로 실행할 수 있습니다.

Xcode 버전 정책을 조정할 때마다 설정을 다시 생성하고 항목별로 검토해야 합니다. 기본값이 바뀔 수 있기 때문입니다. 검토의 핵심은 차이의 개수가 아니라 어떤 차이가 컴파일 입력, 산출물 구조, 서명 경계, 실행 대상을 바꾸는지입니다. 이 증거 체계를 유지하면 빌드 실패 원인을 시스템 전체에서 추측할 필요가 없어지고, 설정 변경도 소스 코드 변경과 마찬가지로 추적할 수 있습니다.

자주 묻는 질문

프로젝트 파일이나 xcconfig만 비교하면 안 되나요?

두 파일은 입력을 보여 줄 뿐입니다. xcodebuild 출력에는 상속, 조건부 값과 명령줄 재정의도 반영되므로 실제 빌드에 사용되는 설정에 더 가깝습니다.

어떤 설정 변경이 파이프라인을 중단해야 하나요?

배포 대상, 지원 아키텍처, Swift 버전, 최적화 수준, 서명 방식과 제품 식별자 변경은 일반적으로 중단 대상입니다. 임시 경로는 먼저 정규화합니다.

독점 Apple Silicon

다음 빌드를 위한 독점 클라우드 Mac 선택

워크로드에 맞춰 Mac mini 모델, 대여 기간 및 리전을 선택하세요. 각 주문에는 독립된 물리 노드가 할당되며, 실제 이용 가능 여부는 콘솔의 실시간 응답을 기준으로 합니다.

클라우드 Mac 요금제 선택