엔지니어링 실무

클라우드 Mac에서 ibtool로 Storyboard와 XIB 사전 검사하기

클라우드 Mac에서 ibtool로 Storyboard와 XIB 사전 검사하기

여러 사람이 Storyboard나 XIB를 동시에 수정하면 XML 병합은 성공한 것처럼 보여도 실제 문제는 전체 아카이브 단계에서야 드러날 수 있습니다. 연결 대상 객체가 삭제되었거나, 인터페이스 파일이 컴파일되지 않거나, 현지화 strings 형식이 손상되거나, 서로 다른 두 모듈의 동일한 이름을 가진 출력물이 덮어쓰일 수 있습니다. ArmVMS의 클라우드 Mac에서 전체 빌드를 실행한 뒤 오류를 확인하기보다, 먼저 Xcode에 포함된 ibtool로 독립적인 사전 검사를 수행하는 편이 효율적입니다.

먼저 ibtool로 검사할 수 있는 범위 파악하기

ibtool은 Interface Builder 리소스를 위한 명령줄 컴파일러입니다. .storyboard.xib를 개별적으로 읽어 XML 구조, 객체 연결, 일부 속성 설정 및 컴파일 과정의 경고를 보고할 수 있습니다. 일반적으로 비즈니스 로직 코드를 먼저 컴파일할 필요가 없으므로 커밋 검사나 CI의 초기 단계에 배치하기 적합합니다.

하지만 전체 빌드를 대체할 수는 없습니다. 사용자 정의 클래스가 실제로 존재하는지, Swift 타입이 일치하는지, 리소스가 올바른 대상에 포함되는지, 최종 링크가 완료되는지는 여전히 xcodebuild로 확인해야 합니다. 먼저 ibtool을 실행하고 통과한 뒤 대상 프로젝트의 빌드와 테스트를 수행하는 것이 적절한 순서입니다.

ibtool은 최종 결과물을 검수하는 도구가 아니라 인터페이스 리소스의 구문 및 구조 검사기로 사용해야 합니다. 빠르게 실패를 감지하는 것이 핵심 가치이며, 전체 빌드 의미 체계를 모두 다루는 도구는 아닙니다.

시작하기 전에 작업에서 실제로 사용하는 개발자 디렉터리를 확인하여 대화형 세션과 자동화 작업이 서로 다른 Xcode를 선택하는 상황을 방지합니다.

xcode-select -p
xcodebuild -version
xcrun --find ibtool

한 시스템에 여러 Xcode가 설치되어 있다면 작업 중에 전역 선택을 반복해서 변경하지 마십시오. 단일 작업에 DEVELOPER_DIR을 명시적으로 설정하고, 사전 검사와 이후 빌드에서 동일한 값을 사용하도록 구성할 수 있습니다.

Storyboard와 XIB 일괄 컴파일하기

다음 스크립트는 프로젝트 안의 인터페이스 파일을 재귀적으로 검색하면서 의존성, 빌드 산출물 및 캐시 디렉터리를 제외합니다. 상대 경로를 기준으로 해시를 생성하므로 서로 다른 모듈에 Main.storyboard가 각각 있어도 출력이 충돌하지 않습니다.

#!/bin/bash
set -euo pipefail

export LANG=C
export LC_ALL=C

ROOT="${1:-$PWD}"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/ibtool-check.XXXXXX")"
trap 'rm -rf "$TMP"' EXIT

status=0

while IFS= read -r -d '' file; do
  rel="${file#"$ROOT"/}"
  key="$(printf '%s' "$rel" | shasum -a 256 | cut -c1-12)"
  log="$TMP/$key.log"

  case "$file" in
    *.storyboard) output="$TMP/$key.storyboardc" ;;
    *.xib) output="$TMP/$key.nib" ;;
    *) continue ;;
  esac

  printf 'Checking %s
' "$rel"

  if ! xcrun ibtool \
    --errors \
    --warnings \
    --notices \
    --compile "$output" \
    "$file" >"$log" 2>&1; then
    cat "$log"
    status=1
  fi
done < <(
  find "$ROOT" \
    \( -path '*/Pods/*' \
       -o -path '*/Carthage/*' \
       -o -path '*/.build/*' \
       -o -path '*/DerivedData/*' \) -prune \
    -o \( -name '*.storyboard' -o -name '*.xib' \) \
    -type f -print0
)

exit "$status"

-print0read -d ''를 함께 사용하면 공백이 포함된 경로도 올바르게 처리할 수 있습니다. 임시 디렉터리는 trap으로 정리되므로 컴파일 산출물이 작업 공간에 남지 않습니다. 실패 로그는 해당 파일에서 오류가 발생했을 때만 출력되어 CI 기록이 대량의 성공 메시지로 뒤덮이는 것을 방지합니다.

소스 디렉터리로 직접 컴파일하지 않기

.storyboardc는 실제로 디렉터리이며 .nib에도 여러 컴파일 결과가 포함될 수 있습니다. 출력을 소스 근처에 기록하면 이후 Git 상태 확인, 리소스 스캔 및 패키징 단계에 영향을 줄 수 있습니다. 모든 사전 검사 산출물은 해당 작업만 사용하는 임시 디렉터리에 저장하고 종료 시 삭제해야 합니다.

현지화 리소스도 함께 검사하기

Base Localization을 사용하는 프로젝트는 일반적으로 기본 Storyboard 하나를 유지하고 언어별 .strings 파일을 관리합니다. 인터페이스 파일은 정상적으로 컴파일되더라도 특정 strings 파일이 따옴표, 이스케이프 문자 또는 충돌 표시 때문에 손상되었을 수 있습니다. 먼저 plutil로 형식을 검사합니다.

find "$PWD" -type f \
  \( -name '*.strings' -o -name '*.stringsdict' \) \
  -not -path '*/Pods/*' \
  -print0 |
while IFS= read -r -d '' file; do
  plutil -lint "$file"
done

기본 인터페이스에서 번역 가능한 키를 검토해야 한다면 임시 목록을 생성할 수 있습니다.

xcrun ibtool \
  --generate-strings-file /tmp/Main.generated.strings \
  App/Base.lproj/Main.storyboard

plutil -lint /tmp/Main.generated.strings

생성된 파일로 사람이 관리하는 번역을 직접 덮어쓰지 마십시오. 생성 순서와 주석은 Xcode 버전에 따라 달라질 수 있습니다. 더 안정적인 방법은 키 집합을 분석하여 새 키가 번역 절차에 포함되었는지, 더 이상 사용하지 않는 키가 여전히 남아 있는지 확인하는 것입니다.

진단 결과를 유지 관리 가능한 게이트로 만들기

사전 검사가 실패하면 현재 단계를 즉시 중단해야 하지만, 경고는 수준별로 처리해야 합니다. 레이아웃 호환성 안내, 접근성 레이블 누락, 오래된 속성에 대한 경고는 기록할 가치가 있습니다. 반면 Xcode 버전이 바뀌면서 새로 추가된 진단은 평가 없이 갑자기 모든 브랜치를 차단해서는 안 됩니다.

현상 일반적인 원인 처리 방법
XML 구문 분석 실패 병합 충돌 표시 또는 파일 잘림 충돌이 발생한 커밋으로 돌아가 수정하고 알 수 없는 노드를 임의로 삭제하지 않기
출력 덮어쓰기 여러 모듈에 동일한 이름의 인터페이스 파일 존재 상대 경로 해시를 사용해 출력 이름 생성
현지화 파일 구문 분석 실패 따옴표, 세미콜론 또는 이스케이프 오류 모든 strings 및 stringsdict에 plutil -lint 실행
로컬에서는 통과하지만 CI에서 실패 Xcode 또는 DEVELOPER_DIR 불일치 로그에 버전을 기록하고 작업 환경 통일
경고 수가 계속 변동 도구 버전 변경 또는 불안정한 검사 범위 제외 디렉터리를 고정하고 새 경고의 차이 검사

소규모 변경에서는 현재 브랜치에서 수정된 인터페이스 파일만 검사할 수 있지만, 기본 브랜치나 예약 작업에서는 전체 저장소를 검사해야 합니다. 증분 검사는 빠른 피드백을 담당하고, 전체 검사는 이름 변경, 삭제 및 모듈 간 조정에서 발생하는 누락을 찾아냅니다. 어느 한쪽이 다른 쪽을 대체해서는 안 됩니다.

전체 빌드로 최종 검증하기

ibtool을 통과한 뒤에는 최소한 한 번 이상 서명에 의존하지 않는 시뮬레이터 빌드를 실행하여 인터페이스 리소스, 코드, 대상 멤버십 및 기타 리소스가 함께 정상적으로 동작하는지 확인해야 합니다.

: "${WORKSPACE:?Set WORKSPACE to the workspace path}"
: "${SCHEME:?Set SCHEME to the shared scheme}"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -destination 'generic/platform=iOS Simulator' \
  CODE_SIGNING_ALLOWED=NO \
  build

최종 게이트에서는 사전 검사와 전체 빌드가 동일한 Xcode를 사용하는지, 스크립트의 제외 디렉터리가 프로젝트의 의존성 디렉터리와 일치하는지, 임시 산출물이 저장소에 들어가지 않는지, 실패 로그에 원래 상대 경로가 남아 있는지 확인해야 합니다. 이렇게 구성하면 인터페이스 리소스 문제는 몇 초 안에 수행되는 검사에서 먼저 드러나고, 실제 프로젝트 컨텍스트가 필요한 문제만 전체 빌드로 넘어가므로 문제 범위도 더욱 명확해집니다.

자주 묻는 질문

ibtool 검사만으로 Xcode 빌드를 대체할 수 있나요?

대체할 수 없습니다. ibtool은 인터페이스 파일 자체의 오류를 빠르게 찾지만 코드 타입, 빌드 설정, 링크와 최종 번들 검증에는 xcodebuild가 필요합니다.

출력 이름에 원본 파일의 상대 경로를 반영해야 하는 이유는 무엇인가요?

여러 모듈에 같은 이름의 Storyboard나 XIB가 있을 수 있기 때문입니다. basename만 사용하면 결과가 덮어써지므로 상대 경로 해시나 디렉터리 구조를 보존해야 합니다.

모든 ibtool 경고를 즉시 실패로 처리해야 하나요?

먼저 현재 경고를 기준선으로 저장하고 새로 추가된 경고만 차단하는 방식이 안전합니다. Xcode 버전에 따라 진단 문구가 달라질 수 있습니다.

ArmVMS 클라우드 Mac

다음 빌드를 위한 독점 Apple Silicon 물리 노드 선택

ArmVMS M4 및 ArmVMS M4 Pro를 일·주·월·분기 단위로 대여하세요. 주문을 만들기 전에 구성, 노드 및 추가 항목을 하나씩 확인할 수 있습니다.

구성을 선택하고 주문 생성