엔지니어링 실무

확장 속성으로 발생한 iOS 서명 오류 진단하기

확장 속성으로 발생한 iOS 서명 오류 진단하기

로컬에서는 아카이브가 정상적으로 생성되지만 클라우드 Mac으로 옮기면 서명 단계에서 resource fork, Finder information, or similar detritus not allowed 오류가 발생할 수 있습니다. 이런 문제는 대개 인증서나 프로비저닝 프로파일이 만료되어서가 아니라 이미지, 스크립트 산출물 또는 복사된 디렉터리에 macOS 확장 속성이 남아 있기 때문에 발생합니다. 이 속성은 리소스가 복사될 때 App 번들에 함께 들어가며, 서명 도구가 엄격한 검사를 수행하는 단계에서야 드러납니다.

먼저 어느 단계에서 실패했는지 확인하기

“서명 실패”라는 메시지만 보고 곧바로 인증서를 다시 만들지 마세요. 먼저 전체 빌드 로그에서 최초의 codesign 오류를 찾고, 실패한 대상이 기본 App인지, Framework인지, 확장 프로그램인지, 리소스 번들인지 기록해야 합니다. 로그에 FinderInfo, ResourceFork 또는 detritus가 명시되어 있다면 파일 메타데이터를 중심으로 조사해야 합니다.

확장 속성은 주로 다음 경로를 통해 유입됩니다.

  • 그래픽 파일 관리자로 저장소에 복사한 이미지, 글꼴 또는 압축 파일
  • 공유 디렉터리에서 압축을 푼 뒤 그대로 커밋한 리소스
  • 빌드 스크립트가 cp -R로 옮긴 사전 생성 디렉터리
  • 버전 관리에는 포함되지 않았지만 작업 디렉터리에 장기간 남아 있던 로컬 산출물
  • 캐시를 복원할 때 파일 메타데이터까지 함께 기록된 종속 항목

DerivedData를 삭제하면 빌드 출력만 제거됩니다. 오염된 속성이 소스나 외부 입력에 있다면 다음 빌드에서도 동일한 문제가 반복해서 발생합니다.

정리 작업을 시작하기 전에 실패한 명령, 대상 경로와 로그를 저장하세요. 그렇지 않으면 빌드가 우연히 다시 성공하더라도 실제 오염원을 확인할 수 없습니다.

읽기 전용 스캔 기준선 만들기

저장소 입력 검사하기

xattr -lr는 경로와 해당 속성을 재귀적으로 나열합니다. 스캔하면서 바로 삭제하지 말고 먼저 결과를 로그에 저장하세요.

set -euo pipefail

ROOT="${SRCROOT:-$PWD}"
REPORT="${TMPDIR:-/tmp}/ios-xattr-source.log"

xattr -lr "$ROOT" > "$REPORT" 2>&1 || true

grep -E \
  'com\.apple\.(FinderInfo|ResourceFork)' \
  "$REPORT" || true

저장소가 크다면 .git, DerivedData와 종속성 캐시를 제외한 뒤 실제 패키징에 사용되는 디렉터리를 하나씩 검사할 수 있습니다. 중요한 것은 발견된 항목의 개수가 아니라 다음 세 가지 질문에 답하는 것입니다. 속성이 어느 파일에 붙어 있는지, 해당 파일을 무엇이 생성했는지, 어떤 Build Phase를 통해 번들에 들어가는지 확인해야 합니다.

발견 위치 우선 확인할 항목 처리 원칙
Assets 또는 리소스 디렉터리 파일 가져오기 방식, 압축 해제 도구 원본 파일 수정
스크립트 출력 디렉터리 cp, ditto 매개변수 스테이징 디렉터리 다시 생성
DerivedData 캐시 복원, 이전 산출물 캐시를 지운 뒤 재현
Framework 내부 다운로드 및 압축 해제 단계 다시 가져온 뒤 검수

아카이브 번들 검사하기

아카이브가 이미 생성되었다면 .app을 직접 스캔하여 오염된 속성이 최종 결과물에 들어갔는지 확인할 수 있습니다.

ARCHIVE_PATH="$PWD/build/App.xcarchive"
APP_PATH="$ARCHIVE_PATH/Products/Applications/App.app"
REPORT="$PWD/build/xattr-app.log"

test -d "$APP_PATH"
xattr -lr "$APP_PATH" > "$REPORT" 2>&1 || true

if grep -Eq 'com\.apple\.(FinderInfo|ResourceFork)' "$REPORT"; then
  echo "forbidden extended attributes found"
  exit 1
fi

여기서는 고정된 예시 경로를 사용했습니다. 실제 파이프라인에서는 아카이브 디렉터리에서 유일한 .app을 찾아야 하며, 후보를 찾지 못하거나 여러 개가 발견되면 즉시 실패하도록 해야 합니다. 그래야 잘못된 번들을 검사하는 일을 방지할 수 있습니다.

추적 가능성을 유지하며 정리하기

처음부터 전체 작업 공간에 xattr -cr를 실행하는 방식은 권장하지 않습니다. 이 명령은 모든 확장 속성을 제거하므로 변경 범위가 불필요하게 넓어지고, 다운로드·압축 해제·캐시 단계에 있는 문제를 숨길 수도 있습니다. 서명을 손상시키는 것으로 확인된 두 가지 속성만 삭제하는 편이 더 안전합니다.

TARGET="$PWD/StagingPayload"

find "$TARGET" -print0 |
while IFS= read -r -d '' item; do
  xattr -d com.apple.FinderInfo "$item" 2>/dev/null || true
  xattr -d com.apple.ResourceFork "$item" 2>/dev/null || true
done

TARGET은 개발자의 원래 작업 공간이 아니라 일회성 스테이징 복사본으로 지정하는 것이 좋습니다. 정리가 끝나면 다시 스캔하세요. 여전히 속성이 발견되면 빌드를 중단해야 하며, 최종 검수 조건에는 || true를 사용하지 마세요.

속성이 압축 파일에서 유입되었다면 압축 해제 후 검수 단계를 수정해야 합니다. 빌드 스크립트가 원인이라면 스크립트가 실행될 때마다 빈 스테이징 디렉터리를 만들고 명시적인 파일 목록만 복사하도록 해야 합니다. 마지막에 재귀 정리 명령 하나만 추가하면 오염은 계속 남아 다른 작업에서 다시 문제를 일으킬 수 있습니다.

아카이브 프로세스에 검사 단계 추가하기

빌드 전에 입력 검사하기

빌드 전 게이트에서는 전체 홈 디렉터리가 아니라 App에 들어갈 리소스 디렉터리만 스캔하면 됩니다. 속성이 발견되면 상대 경로와 속성 이름을 출력한 뒤 0이 아닌 상태로 종료하세요. 그러면 시간이 오래 걸리는 아카이브 작업을 시작하기 전에 실패시킬 수 있고, 최근 변경 사항도 쉽게 추적할 수 있습니다.

검사 스크립트는 버전 관리에 포함하고 실행 환경을 고정해야 합니다.

  1. 스크립트 자체의 위치를 기준으로 저장소 루트 디렉터리를 확인합니다.
  2. 공백이 포함된 경로는 널 문자로 구분합니다.
  3. 보고서는 해당 작업 전용 산출물 디렉터리에 저장합니다.
  4. 스캔 단계에서는 파일을 수정하지 않습니다.
  5. 대상 디렉터리가 없으면 즉시 오류를 반환합니다.

아카이브 생성 후 서명 검증하기

확장 속성 스캔을 통과한 뒤 시스템 서명 도구로 아카이브를 검증합니다.

APP_PATH="$PWD/build/App.xcarchive/Products/Applications/App.app"

codesign --verify \
  --strict \
  --verbose=2 \
  "$APP_PATH"

App 안에 독립적인 Framework나 확장 프로그램이 포함되어 있다면 이러한 중첩 코드 객체도 순회하며 각각 검증해야 합니다. 기본 번들만 검사한 뒤 내부 구성 요소도 안전하다고 가정해서는 안 됩니다. 검증 로그, 아카이브 경로와 소스 커밋 번호를 함께 저장하면 “소스는 같지만 입력 캐시가 다른” 상황을 구분할 수 있습니다.

검수 체크리스트로 조사 마무리하기

신뢰할 수 있는 수정은 다음 조건을 모두 충족해야 합니다.

  • 소스와 외부 입력의 스캔 결과가 보관되어 있습니다.
  • 오염된 파일이 생성되거나 복사된 경로가 확인되었습니다.
  • 정리 작업은 명확히 지정된 속성 또는 일회성 스테이징 복사본에만 적용되었습니다.
  • DerivedData와 관련 캐시를 삭제한 뒤에도 아카이브를 다시 생성할 수 있습니다.
  • 최종 App 번들에 FinderInfo와 ResourceFork가 없습니다.
  • 기본 App과 중첩 코드 객체가 모두 엄격한 서명 검증을 통과합니다.
  • 동일한 커밋이 완전히 새로운 작업 디렉터리에서도 반복해서 통과합니다.

확장 속성 문제를 해결하기 어려운 이유는 명령이 복잡해서가 아니라 소스, 파일 시스템 메타데이터, 캐시와 서명이라는 네 계층에 걸쳐 있기 때문입니다. “먼저 기록하고, 원인을 찾은 뒤 정리하고, 마지막으로 검수하는” 절차를 게이트로 정착시키면 더 이상 담당자가 노드에 직접 로그인해 임시로 처리할 필요가 없습니다. 작업 디렉터리가 바뀌어도 문제를 추적할 단서가 사라지지 않습니다.

자주 묻는 질문

DerivedData를 삭제해도 서명 오류가 반복되는 이유는 무엇인가요?

소스 리소스, 다운로드 파일 또는 스크립트가 복사하는 디렉터리에 FinderInfo나 ResourceFork가 남아 있으면 새 빌드에도 다시 포함됩니다. 먼저 입력 파일을 검사해야 합니다.

저장소 전체에 xattr -cr을 실행해도 되나요?

권장하지 않습니다. 먼저 대상 경로를 기록하고 FinderInfo와 ResourceFork만 삭제하세요. 모든 속성을 재귀적으로 지우는 작업은 폐기 가능한 스테이징 복사본에서만 수행하는 편이 안전합니다.

정리한 아카이브는 어떻게 검증하나요?

App 번들에서 대상 확장 속성이 발견되지 않는지 검사하고 codesign --verify --strict를 실행합니다. 검사 로그와 아카이브 체크섬도 함께 보관하세요.

NowMini M4

전용 물리 노드에서 다음 작업 실행

M4, 16GB RAM, 256GB SSD를 탑재한 클라우드 Mac을 사용하고, 작업 주기에 맞춰 싱가포르, 도쿄, 서울 또는 홍콩 노드를 선택하세요.

클라우드 Mac 지금 대여하기