VMArm 기술 기록

클라우드 Mac에서 iOS 주문형 리소스 빌드와 검증

클라우드 Mac에서 iOS 주문형 리소스 빌드와 검증

iOS 프로젝트의 스테이지, 고해상도 텍스처 또는 오프라인 모델을 On-Demand Resources로 옮기면 설치 패키지 크기는 대개 크게 줄어듭니다. 하지만 빌드 성공이 곧 리소스 사용 가능을 의미하지는 않습니다. 가장 흔한 문제는 컴파일 오류가 아니라 태그 누락, 테스트 패키지에 모든 리소스가 의도치 않게 포함되는 상황, 또는 배포 산출물의 리소스 팩과 코드에서 요청하는 항목이 일치하지 않는 경우입니다. 클라우드 Mac에서는 이러한 검사를 독립 작업으로 표준화하기 좋습니다. 매번 깨끗한 작업 공간에서 아카이브하고, 매니페스트와 체크섬을 보관한 다음, 실제 요청을 한 차례 실행할 수 있습니다.

검증 가능한 범위부터 정의하기

ODR 검증은 최소한 프로젝트 설정, 내보낸 산출물, 런타임 요청의 세 계층을 포함해야 합니다. 한 계층만 확인하면 사각지대가 생깁니다.

프로젝트 계층에서는 Target에 On-Demand Resources가 활성화되어 있는지, 리소스 카탈로그의 각 태그에 명확한 용도가 있는지 확인해야 합니다. 산출물 계층에서는 .assetpack, 매니페스트 파일, 파일 해시를 검사합니다. 런타임 계층에서는 최초 요청, 리소스 해제, 재요청을 검증합니다.

“앱이 실행된다”는 사실만으로는 ODR 검증 기준을 충족할 수 없습니다. 한 번도 접근하지 않은 스테이지 리소스가 완전히 누락되어도 첫 화면 실행에는 지장이 없을 수 있습니다.

각 태그에 대해 담당자, 트리거 화면, 예상 리소스 유형, 최초 설치에 필수인지 여부라는 네 가지 정보를 기록하는 것이 좋습니다. 이 정보는 Xcode 화면에만 남겨 두지 말고 저장소의 일반 텍스트 매니페스트로 관리해야 합니다.

안정적인 태그로 리소스 팩 분할 단위 제어하기

태그를 폴더 수와 일대일로 대응시키지 마십시오. 너무 세분화하면 작은 팩과 빈번한 요청이 대량으로 발생하고, 너무 크게 묶으면 사용자가 한 화면을 위해 관련 없는 리소스 묶음 전체를 내려받아야 합니다. 더 안정적인 방법은 level-01, tutorial-audio, model-basic처럼 사용자 흐름을 기준으로 나누고, 코드의 요청 이름과 리소스 카탈로그 태그를 정확히 일치시키는 것입니다.

태그 매니페스트에 병합 조건 적용하기

사용 가능한 태그 목록을 저장소에 보관할 수 있습니다.

level-01
level-02
tutorial-audio
model-basic

빌드 스크립트는 프로젝트 파일이나 생성된 리소스 정보에서 태그를 추출하고 정렬한 뒤 이 목록과 비교합니다. 태그를 추가할 때는 반드시 매니페스트도 변경해야 하며, 태그를 삭제할 때는 코드의 NSBundleResourceRequest 호출도 함께 확인해야 합니다. 이렇게 하면 철자 오류를 런타임이 아닌 병합 단계에서 발견할 수 있습니다.

태그 이름에는 안정적인 ASCII 문자만 사용하고 공백, 대소문자 혼용, 임시 버전 번호는 피해야 합니다. 리소스 업데이트는 콘텐츠 버전이나 체크섬으로 표현하고, level-final-v2-new처럼 유지 관리하기 어려운 이름을 계속 만들지 마십시오.

아카이브 및 내보내기 조건 고정하기

동일한 커밋에는 고정된 workspace, scheme, Release 구성, 내보내기 옵션을 사용해야 합니다. VMArm 노드의 작업은 이번 빌드의 출력 디렉터리를 먼저 비울 수 있지만, 전역 캐시를 일괄 삭제해서는 안 됩니다. 그렇게 하면 변경 원인이 리소스인지 환경인지 판단하기 어렵습니다.

set -euo pipefail

: "${WORKSPACE:?WORKSPACE is required}"
: "${SCHEME:?SCHEME is required}"

ROOT="$PWD"
ARCHIVE_PATH="$ROOT/out/Game.xcarchive"
EXPORT_DIR="$ROOT/out/export"

rm -rf "$ARCHIVE_PATH" "$EXPORT_DIR"
mkdir -p "$EXPORT_DIR"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath "$ARCHIVE_PATH" \
  clean archive

xcodebuild \
  -exportArchive \
  -archivePath "$ARCHIVE_PATH" \
  -exportPath "$EXPORT_DIR" \
  -exportOptionsPlist "$ROOT/ci/ExportOptions.plist"

로컬 검증 경로에서는 embedOnDemandResourcesAssetPacksInBundletrue로 설정해 리소스 팩의 내용과 요청 로직이 올바른지 먼저 입증할 수 있습니다. 프로덕션 경로에서는 실제 배포 방식에 맞는 내보내기 구성을 사용해야 하며, 로컬 임베드 설정을 그대로 사용해서는 안 됩니다. 테스트 패키지가 배포 예정 패키지를 덮어쓰지 않도록 두 경로의 산출물은 별도 디렉터리에 저장해야 합니다.

산출물을 세 계층으로 검사하기

첫 번째 계층에서는 파일이 존재하는지 확인합니다. 두 번째 계층에서는 plist를 파싱할 수 있는지 검사합니다. 세 번째 계층에서는 모든 리소스 파일에 대해 안정적인 체크섬을 생성하여 동일한 커밋의 두 빌드를 비교할 수 있게 합니다.

set -euo pipefail

ARCHIVE_PATH="$PWD/out/Game.xcarchive"
EXPORT_DIR="$PWD/out/export"
REPORT_DIR="$PWD/out/report"

mkdir -p "$REPORT_DIR"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -name '*.assetpack' \
  -print | LC_ALL=C sort > "$REPORT_DIR/assetpacks.txt"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -name 'AssetPackManifest.plist' \
  -exec plutil -lint {} \; > "$REPORT_DIR/plist-lint.txt"

find "$ARCHIVE_PATH" "$EXPORT_DIR" \
  -type f \
  \( -name '*.car' -o -name '*.plist' -o -name '*.assetpack' \) \
  -exec shasum -a 256 {} \; |
  LC_ALL=C sort > "$REPORT_DIR/checksums.txt"

test -s "$REPORT_DIR/assetpacks.txt"

.assetpack이 실제로 디렉터리라면 마지막 해시 명령은 디렉터리 내용을 직접 계산하지 않습니다. 따라서 해당 디렉터리 아래의 일반 파일도 검사 대상에 포함해야 합니다. 보고서에는 커밋 번호, Xcode 버전, scheme, 내보내기 구성 파일의 해시를 기록하되 서명용 개인 키, 토큰 또는 전체 자격 증명 경로를 기록해서는 안 됩니다.

두 보고서를 비교할 때는 먼저 “파일 순서 변경”과 “콘텐츠 변경”을 구분해야 합니다. 정렬 방식을 통일하면 전자는 제거할 수 있습니다. 동일한 커밋인데도 해시가 계속 달라진다면 생성 스크립트에 타임스탬프, 작업 공간의 절대 경로 또는 임의 식별자가 기록되는지 확인하십시오.

런타임 검증 및 문제 진단

런타임 테스트는 깨끗한 상태에서 시작해야 합니다. 검증용 패키지를 설치한 후 최초 설치 태그가 아닌 태그 하나를 요청하고, 요청 시작, 접근 허용, 리소스 해제라는 세 이벤트를 기록합니다. 그런 다음 앱을 종료하고 테스트 환경을 정리한 뒤 다시 실행하여 이전 캐시 덕분에 성공한 것이 아님을 확인합니다.

일반적인 증상별 점검 순서

요청이 즉시 실패하면 먼저 코드의 태그와 리소스 카탈로그 태그를 대조합니다. 요청이 장시간 대기하면 리소스 팩이 올바르게 내보내졌는지와 테스트 네트워크 상태를 확인합니다. 로컬 임베드에서는 성공하지만 프로덕션 경로에서 실패한다면 두 ExportOptions와 리소스 호스팅 구성을 중점적으로 비교합니다. 일부 리소스만 누락된다면 같은 이름의 파일이 여러 태그에 분류되었는지, 또는 해당 리소스가 여전히 기본 번들에 속해 있는지 확인합니다.

무한 재시도로 오류를 감추지 마십시오. 요청에 명확한 타임아웃을 설정하고, 실패 로그에는 태그, 오류 도메인, 오류 코드, 단계만 기록해야 합니다. 접근 토큰이나 민감한 경로는 출력하지 마십시오. 검증이 끝나면 보고서, 내보내기 구성 해시, 실패 로그를 보관하고 임시 패키지와 테스트 캐시는 삭제합니다. 그러면 다음 ODR 변경에서 차이가 발생했을 때 Xcode가 무엇을 했는지 다시 추측하는 대신 산출물을 직접 비교할 수 있습니다.

자주 묻는 질문

개발용 빌드에는 ODR 리소스 팩을 포함해야 하나요?

첫 번째 로컬 검증 단계에서는 포함하는 편이 좋습니다. embedOnDemandResourcesAssetPacksInBundle을 true로 설정해 패키징 문제를 먼저 확인하고, 실제 배포 설정을 사용하는 별도 내보내기로 전달 경로를 검증합니다.

IPA 크기만 확인하면 ODR 검증이 끝나나요?

아닙니다. assetpack 개수와 태그 매핑, AssetPackManifest.plist, 파일 체크섬을 확인하고 캐시를 지운 뒤 리소스를 다시 요청해 보존된 데이터의 영향을 제거해야 합니다.

독점 물리 Mac 노드

다음 Xcode 빌드에 사용할 상시 가동 클라우드 Mac을 선택하세요

칩, 메모리, 저장 공간, 결제 주기 및 노드 지역을 확인한 후 구성 절차를 시작하세요. 실제 사용 가능 여부는 콘솔에서 실시간으로 반환되는 상태를 기준으로 합니다.

클라우드 Mac 구성 선택