Xcode를 열지 않고 Mac·iOS 앱 빌드와 배포하기

7 hours ago 2
  • 초기 Apple 계정·인증서 설정을 마치면 Xcode.app 내부 CLI 도구만으로 Mac과 iOS 앱의 빌드, 서명, 배포를 자동화할 수 있음
  • project.yml에서 XcodeGen으로 .xcodeproj를 재생성하고, xcodebuild, notarytool, stapler, devicectl로 프로젝트 생성부터 Mac 공증과 iPhone 설치까지 처리함
  • Mac 배포는 scripts/release.sh 하나로 아카이브, Developer ID 서명, 공증, 티켓 스테이플링, Gatekeeper 검증, /Applications 설치를 순서대로 실행하며 실패 시 즉시 중단됨
  • 빠른 컴파일과 테스트에는 CODE_SIGNING_ALLOWED=NO를 사용할 수 있지만, 이 임시 서명 빌드는 Gatekeeper를 통과하지 못하고 iCloud KVS와 App Group 권한도 실제 팀 ID에 결합되지 않음
  • 서명 개인 키와 공증 암호는 로그인 키체인에 보관하고, CLAUDE.md 또는 AGENTS.md에 명령과 배포 규칙을 기록하면 Claude Code 같은 에이전트가 전체 워크플로를 반복 실행할 수 있음

Xcode GUI 없이 처리할 수 있는 범위

  • Xcode 자체는 설치해야 하지만 실행할 필요는 없으며, xcodebuild, notarytool, stapler, devicectl은 모두 Xcode.app 내부의 셸 도구로 독립 실행됨
  • Apple ID 로그인, Developer ID 인증서 생성, 공증 암호 저장 같은 최초 설정에는 GUI 또는 대화형 터미널이 필요함
  • 초기 설정을 끝내면 프로젝트 생성, 빌드, 아카이브, 서명된 앱 내보내기, 공증, 검증, 설치, 실제 iPhone 배포까지 마우스 없이 처리 가능함
  • 설정이나 스크립트 작성이 막힐 때는 Claude Code 또는 다른 LLM 코딩 도구에 작업 요구사항과 프로젝트별 값을 제공해 워크플로를 만들 수 있음

전체 Xcode 도구 체인 선택

  • xcode-select -p의 결과가 /Applications/Xcode.app/Contents/Developer인지 먼저 확인해야 함
  • 독립형 Command Line Tools가 선택돼 있다면 다음 명령으로 전체 Xcode 도구 체인을 지정함
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
  • xcode-select --install로 설치하는 /Library/Developer/CommandLineTools에는 clang과 git이 있지만, iOS SDK, notarytool, devicectl 등 전체 앱 개발 도구는 들어 있지 않음
  • Xcode가 이미 설치돼 있다면 별도의 Command Line Tools 패키지는 필요하지 않음
  • 라이선스 동의와 추가 구성요소 설치도 CLI에서 처리할 수 있음
sudo xcodebuild -license accept sudo xcodebuild -runFirstLaunch

XcodeGen으로 프로젝트 재현하기

  • XcodeGen은 Xcode 프로젝트 설정을 YAML로 관리하고 빌드할 때마다 .xcodeproj를 다시 생성함
  • GitHub에서 다운로드하거나 Homebrew로 설치할 수 있음
brew install xcodegen
  • .xcodeproj는 macOS에서 파일처럼 보이는 폴더이며, Xcode가 내부 파일과 참조를 계속 바꾸기 때문에 Git 저장소에서 충돌이나 불필요한 변경이 생길 수 있음
  • project.yml만 Git에 커밋하고 생성된 .xcodeproj는 무시하면 프로젝트 설정을 재현 가능한 상태로 유지할 수 있음
xcodegen generate

Apple 개발자 계정과 인증서 설정

  • Xcode의 Settings → Accounts에서 Apple ID를 추가해야 함
  • 앱 배포와 공증에는 유료 Apple Developer 계정이 필요함
  • Settings → Accounts → Apple ID → Manage Certificates… → + → Developer ID Application에서 배포용 인증서를 생성함
  • Apple DevelopmentDeveloper ID Application은 용도가 다름
    • Apple Development는 자신의 기기에서 실행하거나 iPhone으로 전송해 로컬 디버깅할 때 사용함
    • Developer ID Application은 다른 사람의 Mac에서도 Gatekeeper를 통과하는 공증된 .app을 배포할 때 필요함
  • Developer ID Application 인증서를 만들면 인증서와 대응하는 개인 키가 로그인 키체인에 설치됨
  • 실제 서명에는 개인 키가 사용되며 다시 다운로드할 수 없으므로, 삭제하지 말고 키체인을 백업해야 함

공증 자격 증명을 키체인에 저장하기

  • Mac 앱 공증은 서명된 앱을 Apple에 업로드해 악성 코드 검사를 받는 과정임
  • notarytool은 키체인에 저장된 프로필로 인증하며, 프로필을 처음 만들 때 앱 전용 암호를 대화형으로 입력해야 함
xcrun notarytool store-credentials App-Name \ --apple-id "you@example.com" \ --team-id YOUR-TEAM-ID
  • 공증 프로필은 다른 앱의 것을 빌려 쓰지 말고 앱 이름에 맞춰 생성해야 다른 환경에서 조용히 실패하는 상황을 피할 수 있음
  • 앱 전용 암호는 Apple ID 암호와 다르며, Apple ID 사이트의 Sign-In & Security → App-Specific Passwords에서 생성함
  • Apple ID 암호를 바꾸면 앱 전용 암호가 별도 경고 없이 만료될 수 있음
    • 공증 중 401 invalid credentials가 발생하면 설정 전체를 의심하기 전에 앱 전용 암호 만료부터 확인해야 함
  • 프로필 저장 여부는 다음 명령으로 검증함
xcrun notarytool history --keychain-profile App-Name
  • 앱 전용 암호를 Claude Code가 접근할 수 있는 1Password 보관함에 저장하면 새 앱의 공증 프로필 생성도 에이전트에 맡길 수 있음
  • --password로 암호 입력까지 자동화할 수 있지만 셸 기록에 암호가 남을 수 있으므로, 한 번 직접 입력한 뒤 키체인이나 1Password에 보관하는 방식을 사용함

로컬 서명 설정과 비밀 정보 분리

  • 실제 서명에 필요한 팀 ID와 번들 접두사는 Local.xcconfig에 저장함
cp Local.xcconfig.example Local.xcconfig BUNDLE_PREFIX = your.real.prefix DEVELOPMENT_TEAM = YOUR-TEAM-ID
  • Local.xcconfig는 .gitignore에 추가해 프로젝트별 로컬 설정이 저장소에 들어가지 않도록 함
  • 서명 개인 키는 로그인 키체인에, 공증용 앱 전용 암호는 notarytool 키체인 프로필에 남으므로 비밀 정보가 Git에 기록되지 않음

release.sh로 Mac 배포 자동화하기

  • 저장소의 scripts/release.sh가 Mac 앱의 전체 배포 파이프라인을 한 명령으로 실행함
./scripts/release.sh
  • 스크립트는 다음 순서로 동작함

    1. xcodegen과 공증 프로필이 존재하는지 사전 확인
    2. xcodegen generate로 프로젝트 재생성
    3. xcodebuild archive로 Release 아카이브 생성
    4. ExportOptions.plist와 xcodebuild -exportArchive로 Developer ID 서명 앱 내보내기
    5. 앱을 ZIP으로 묶어 notarytool submit --wait로 Apple에 제출
    6. stapler staple로 공증 티켓 부착
    7. spctl로 Gatekeeper 허용 여부 검증
    8. 기존 프로세스를 종료하고 앱을 /Applications에 복사
    9. lsregister로 설치된 앱 등록
    10. 설치된 번들의 티켓과 Gatekeeper 상태 재검증
  • 모델이 일반적인 배포 절차를 알고 있더라도 스킴 이름, 팀 ID, 공증 프로필 이름, 설치 위치 같은 프로젝트별 정보는 사용자가 제공해야 함

  • 첫 스크립트가 실패하면 실행 결과를 바탕으로 수정하는 반복 과정이 필요하지만, 워크플로가 안정된 뒤에는 별도 수정 없이 배포 명령만 실행할 수 있음

  • 실패와 불완전한 배포 방지

    • set -euo pipefail은 명령 하나라도 실패하면 스크립트를 즉시 중단해 불완전한 상태를 성공으로 처리하지 않도록 함
    • cd "$(dirname "$0")/.."는 스크립트를 어느 디렉터리에서 호출하더라도 저장소 루트로 이동시킴
    • 사전 검사 블록은 시간이 오래 걸리는 아카이브 전에 xcodegen과 공증 프로필을 확인함
    • 내보낸 앱뿐 아니라 /Applications에 복사된 앱도 다시 검사해 복사 과정에서 번들이 손상되는 경우를 잡아냄
    • 다른 공증 프로필이 필요하면 환경 변수로 덮어쓸 수 있음
    TZED_NOTARY_PROFILE=<name> ./scripts/release.sh

에이전트에 배포 규칙 전달하기

  • release.sh가 한 번의 명령으로 배포를 수행한다면, CLAUDE.md나 AGENTS.md는 에이전트가 세션마다 별도 지시 없이 그 명령을 사용하도록 만듦
  • 문서에는 프로젝트 재생성, 단위 테스트, 빠른 macOS 빌드, 실제 배포 명령을 기록함
xcodegen generate swift test xcodebuild -project YOUR-APP-NAME.xcodeproj \ -scheme YOUR-APP-NAME-macOS \ -destination 'platform=macOS' \ CODE_SIGNING_ALLOWED=NO build ./scripts/release.sh
  • 두 빌드 경로의 차이도 문서에 명시해야 함
    • CODE_SIGNING_ALLOWED=NO 빌드는 CI와 빠른 로컬 확인에 적합함
    • 실제 메뉴 막대 앱 배포, 격리 속성 통과, iCloud 동기화에는 Developer ID 서명과 공증을 수행하는 release.sh가 필요함
  • 한 앱에서 만든 방식을 다른 저장소에서 참조하도록 지시하면 Claude Code가 이후 앱에도 같은 설정을 복제할 수 있음

빠른 무서명 빌드와 테스트

  • Swift Package Manager 기반 단위 테스트는 Xcode 빌드 없이 실행함
swift test
  • macOS 앱의 빠른 컴파일 확인은 서명을 끈 상태로 수행함
xcodebuild -project TZed.xcodeproj \ -scheme TZed-macOS \ -destination 'platform=macOS' \ CODE_SIGNING_ALLOWED=NO build
  • iOS 앱과 위젯 확장의 시뮬레이터 빌드도 같은 방식으로 실행 가능함
xcodebuild -project TZed.xcodeproj \ -scheme TZed-iOS \ -destination 'generic/platform=iOS Simulator' \ CODE_SIGNING_ALLOWED=NO build
  • CODE_SIGNING_ALLOWED=NO로 만든 임시 서명 빌드는 컴파일과 시뮬레이터 실행에 사용할 수 있음
  • 이 빌드는 Gatekeeper가 거부하며, 팀 접두사가 없어 iCloud KVS와 App Group 같은 권한이 결합되지 않으므로 배포용으로는 사용할 수 없음

실제 iPhone에 헤드리스 배포하기

  • iOS 기기 배포에는 Mac 앱과 같은 공증 단계가 없으며, xcodebuild와 devicectl을 사용함
  • 실제 기기용 앱은 Apple Development 인증서와 개발 프로비저닝 프로필로 빌드하고 서명함
xcodebuild -project TZed.xcodeproj \ -scheme TZed-iOS \ -destination 'generic/platform=iOS' \ -allowProvisioningUpdates \ -derivedDataPath build/ios \ archive -archivePath build/TZed-iOS.xcarchive
  • 생성된 .app은 연결된 기기의 UDID를 지정해 설치함
xcrun devicectl device install app \ --device <DEVICE-UDID> build/ios/…/TZed.app
  • devicectl list devices는 연결되고 페어링된 기기와 UDID를 보여줌
  • -allowProvisioningUpdates를 지정하면 필요한 개발 프로비저닝 프로필을 가져옴

GUI 없이 코드 서명이 작동하는 방식

  • Developer ID Application 인증서를 만들 때 Apple은 인증서를 발급하고, Mac은 대응하는 개인 키를 생성해 둘 다 로그인 키체인에 저장함

  • xcodebuild가 호출하는 codesign은 개인 키로 바이너리를 서명하고, Apple 루트 인증서로 이어지는 인증서를 앱에 포함해 서명자를 검증할 수 있게 함

  • signingStyle: automatic을 사용하면 xcodebuild가 팀 ID에 맞는 인증서를 선택하고 필요한 프로비저닝 프로필을 Apple에서 가져옴

  • 따라서 프로비저닝 프로필을 저장소에 커밋할 필요가 없음

  • 권한, 공증, 스테이플링

    • 각 타깃의 .entitlements 파일에는 샌드박스, 네트워크 클라이언트, iCloud KVS, App Group 같은 권한이 들어 있음
    • 권한은 실제 팀 ID로 서명할 때 결합되므로 임시 서명 빌드에서는 iCloud와 App Group이 정상 작동하지 않음
    • 서명과 공증은 서로 다른 단계
    • 서명은 누가 앱을 만들었는지 증명함
    • 공증은 Apple이 서명된 앱을 악성 코드 검사한 뒤 티켓을 발급하는 절차임
    • 스테이플링은 그 티켓을 앱에 붙여 오프라인에서도 Gatekeeper가 신뢰하도록 함
    • 숨겨진 UI를 사용하는 메뉴 막대 앱인 LSUIElement 앱에서는 공증이 XProtect의 탐지를 피하는 데 필요함
  • 수동 검증 명령

    codesign -dv --verbose=4 /Applications/TZed.app spctl -a -vvv -t exec /Applications/TZed.app stapler validate /Applications/TZed.app
    • codesign은 서명자와 인증서를 확인함
    • spctl은 Gatekeeper가 실행을 허용할지 검사함
    • stapler validate는 공증 티켓이 부착됐는지 검증함

에이전트가 실행하는 표준 도구

  • 별도의 빌드용 MCP 서버나 전용 플러그인은 필요하지 않으며, Claude Code는 비대화형 셸에서 표준 CLI를 실행함
  • 사용하는 도구는 xcodebuild, xcrun notarytool, xcrun stapler, spctl, codesign, devicectl, xcodegen, swift임
  • CLAUDE.md는 공증 프로필 명명 규칙, 빠른 빌드와 실제 배포의 차이, 배포 시 release.sh를 사용해야 한다는 규칙을 에이전트에 전달함
  • 최초 notarytool store-credentials만 대화형 단계로 남기고 이후 과정은 모두 자동화할 수 있음

Xcode GUI 작업을 CLI로 대체하기

  • Xcode가 관리하는 .xcodeproj 생성은 project.yml과 xcodegen generate로 대체함
  • ⌘B 또는 Run 버튼은 xcodebuild … build에 대응함
  • Product → Archive는 xcodebuild … archive로 처리함
  • Organizer의 앱 배포는 xcodebuild -exportArchive로 대체 가능함
  • Organizer 업로드는 xcrun notarytool submit --wait로 수행함
  • 자동 티켓 부착에는 xcrun stapler staple을 사용함
  • /Applications로 드래그 앤드 드롭하는 설치 과정은 cp -R과 lsregister로 처리함
  • 실제 iPhone의 Run 동작은 xcodebuild archive와 devicectl device install로 대체함
  • Xcode와 XcodeGen 설치, 최초 자격 증명 설정, release.sh와 CLAUDE.md 작성까지 끝내면 이후 새 빌드 배포는 한 문장 또는 한 명령으로 실행할 수 있음
Read Entire Article