공증 제출은 성공했는데 배포 파일이 사용자 Mac에서 정상 실행되는지 확신하기 어렵습니다.
해결은 Developer ID로 서명한 배포 산출물을 원격 Mac CI에서 notarytool로 제출하고, 처리 결과와 티켓을 확인한 뒤 실제 배포 파일까지 검증하는 것입니다.
이 글은 Mac App Store 밖에 앱을 배포하는 독립 개발자, 빌드 엔지니어, DevOps 담당자를 위한 안내입니다.
Archive·내보내기·배포 작업의 경계를 정하고, CI 자격 증명을 보호하며, 공증 결과를 재현 가능한 증거로 남기는 방법을 설명합니다.
먼저 정할 범위: Developer ID 배포와 공증은 별도 흐름입니다
이 절차는 Developer ID로 서명해 Mac App Store 외부에 배포하는 macOS 앱을 대상으로 합니다. 공증은 App Review와 같지 않으며, Mac App Store 제출 절차의 일부로 취급해서도 안 됩니다. Apple은 공증과 Mac App Store 배포를 구분해 안내합니다. Apple의 macOS 소프트웨어 공증 안내를 기준으로 대상 배포 경로부터 고정하세요.
완료 기준은 단순히 업로드 요청이 받아들여지는 데 있지 않습니다. 서명된 산출물, 제출 기록, 처리 결과, 필요한 티켓 처리, 최종 배포 파일의 검증이 한 흐름으로 연결되어야 합니다. 서버가 제출물을 접수했더라도 처리 결과가 확정되지 않았거나, 배포 파일에서 티켓과 서명을 확인하지 못했다면 출시는 완료된 것으로 볼 수 없습니다.
Apple은 2023년 11월 1일부터 altool 또는 Xcode 13 및 이전 버전으로 전송하는 공증 업로드를 받지 않는다고 공지했습니다. 기존 스크립트가 아직 해당 경로를 사용한다면, 이를 notarytool 기반 흐름으로 옮기고 Apple의 공증 도구 마이그레이션 안내와 대조하세요.
첫 단계: 서명과 추적 가능한 산출물을 준비합니다
원격 Mac CI에서 공증을 안정적으로 반복하려면 제출 전에 입력물을 고정해야 합니다. Developer ID 서명, Hardened Runtime, 타임스탬프, 앱 안에 포함된 코드의 서명 상태를 확인합니다. 구체적인 서명 옵션과 요구 조건은 앱의 구성에 따라 달라지므로, 임의의 인증서 이름이나 빌드 설정을 복사하지 말고 Apple의 Mac 배포용 코드 서명 문서에 맞춰 프로젝트 설정을 확인하세요.
Archive, 내보낸 앱, 최종 배포 패키지는 서로 다른 산출물입니다. 각 파일의 역할을 혼동하면 Archive에서는 서명이 정상이어도 압축이나 설치 패키징 뒤에 변경된 최종 파일을 검증하지 못할 수 있습니다. CI 기록에는 적어도 소스 커밋, 산출물 식별 정보, 서명 확인 결과, 공증 제출 식별 정보를 연결해 둡니다.
| 단계 | 입력 또는 결과물 | CI에 남길 확인 증거 |
|---|---|---|
| Archive | 빌드 설정과 소스 커밋으로 생성한 보관 결과 | 작업 식별 정보와 빌드 기록 |
| 내보내기 | 배포용으로 서명된 앱 또는 구성 요소 | 서명 확인 결과와 내보내기 기록 |
| 패키징 | 앱을 담은 압축 파일, 디스크 이미지 또는 설치 패키지 | 최종 파일 식별 정보와 패키징 작업 기록 |
각 형식의 포장 조건과 배포 준비 항목은 Apple의 Mac 소프트웨어 패키징 안내를 참고합니다. 패키징이 끝난 뒤 앱을 다시 수정하거나 서명에 영향을 주는 작업을 했다면, 이전 단계의 검사 결과를 최종 파일 검증으로 대신하지 마세요.
두 번째 단계: 원격 Mac에 도구와 인증 경로를 연결합니다
먼저 CI에서 선택한 Xcode 또는 Command Line Tools 환경이 notarytool을 실행할 수 있는지 확인합니다. 개발자 로컬 Mac에서 성공한 사실만으로 원격 노드의 도구 경로와 선택된 Xcode까지 검증된 것은 아닙니다. 실제 CI 작업에서 xcrun notarytool을 호출하고, 실행 환경을 로그에 남기되 자격 증명은 출력하지 않습니다.
Apple은 스크립트 기반 공증 흐름에서 notarytool과 사용자 지정 작업 구성을 안내합니다. 사용자 지정 공증 작업에 대한 Apple 문서를 참고해 제출 단계를 빌드 이후 작업으로 분리하면, 빌드 실패와 공증 실패를 각각 추적하기 쉽습니다.
CI에서 공증 자격 증명은 어디에 두나요?
Keychain 프로파일은 notarytool이 인증 정보를 참조하는 방식이고, CI의 비밀 관리 기능은 그 정보가 작업 환경에 전달되는 경로와 권한을 통제하는 역할을 합니다. 둘을 같은 저장소로 생각하지 마세요. 프로파일 생성과 인증 매개변수는 Apple의 공증 작업 안내에 따라 구성하고, 실제 비밀은 접근 권한을 제한한 저장소에서 실행 시점에만 주입합니다.
저장소에 자격 증명을 넣거나, 명령행 인수·디버그 로그·실패 알림에 비밀 값이 남게 해서는 안 됩니다. Team ID, 계정 식별자, 인증 키와 프로파일 이름도 문서 예시에서는 자리표시자로 관리합니다. 작업이 끝난 뒤 비밀이 남는 임시 파일과 실행 환경을 정리하고, 누가 발급·교체·폐기 책임을 맡는지도 운영 문서에 정합니다.
세 번째 단계: 제출 기록과 처리 결과를 함께 확인합니다
제출 작업에서는 CI가 만든 최종 대상 파일을 명시적으로 전달합니다. 아래 예시는 값이 노출되지 않도록 자리표시자만 사용합니다. 실제 옵션과 인증 방식은 선택한 구성에 맞춰 Apple의 사용자 지정 공증 흐름 문서에서 확인하세요.
xcrun notarytool submit "$PACKAGE_PATH" \
--keychain-profile "$PROFILE_NAME" \
--wait
작업 로그에는 제출 식별 정보와 명령의 종료 상태를 남깁니다. 다만 제출 명령이 끝났다는 사실만으로 모든 후속 검증이 끝난 것은 아닙니다. 처리 결과가 실패이거나 경고가 포함되어 있으면 해당 제출의 로그를 보존하고, 실패 내용을 서명·권한·패키지 형식·서비스 응답으로 나눠 조사합니다. Apple의 공증 문제 해결 문서는 공증 결과를 확인하고 원인을 분류할 때 참고할 수 있습니다.
notarytool 제출이 끝난 뒤 어떤 결과를 확인하나요?
제출 식별 정보, 처리 상태, 관련 로그를 함께 확인합니다. 상태가 성공으로 표시되더라도 최종 배포 파일의 서명과 티켓까지 확인해야 합니다. 결과 조회와 제출 관리에는 Apple Notary API 문서가 제공하는 상태 정보와 작업 흐름을 참고하되, CI가 기록한 대상 파일과 조회한 제출 기록이 같은 작업인지 대조하세요.
오류 하나만 보고 원인을 단정하는 것도 피해야 합니다. 예를 들어 인증 오류, 서명 검증 실패, 파일 형식 문제, 서비스 응답 문제는 각각 다른 증거를 요구합니다. 오류가 생기면 해당 작업의 입력 파일, 제출 식별 정보, 관련 로그를 먼저 묶고, 동일한 파일을 재제출할지 산출물을 다시 만들지 판단합니다.
네 번째 단계: 배포 형식에 맞춰 티켓을 처리합니다
공증 결과와 티켓 처리는 별개의 확인 항목입니다. 앱, 압축 파일, 디스크 이미지, 설치 패키지처럼 전달 형식이 다르면 공증 대상으로 제출할 항목과 티켓을 처리할 대상도 달라질 수 있습니다. 모든 파일에 같은 stapler 동작을 적용하지 말고, 지원 형식과 절차를 Apple의 공증 및 배포 문서에서 확인합니다.
지원되는 산출물에 티켓을 부착하는 경우에는 CI에서 다음과 같이 처리할 수 있습니다. 실행 대상과 옵션은 실제 배포 형식에 맞춰 공식 문서에서 검증해야 합니다.
xcrun stapler staple "$DISTRIBUTION_ARTIFACT"
xcrun stapler validate "$DISTRIBUTION_ARTIFACT"
앱 공증 뒤에 티켓이 실제로 부착됐는지 어떻게 확인하나요?
공증 처리 상태만 보지 말고 배포에 사용할 파일을 대상으로 티켓 검증을 수행합니다. 그 뒤 서명 검사도 다시 실행하고, 다운로드 또는 설치 후 앱 실행까지 확인합니다. 티켓 검증은 사용자에게 전달할 최종 파일의 설치·실행 시험을 대신하지 않습니다.
배포 파일을 다시 압축하거나 이름만 바꿨더라도 형식에 따라 확인 대상이 달라질 수 있습니다. CI에서는 제출한 파일과 실제 업로드하는 파일을 서로 다른 산출물로 기록하고, 최종 전달 파일에서 검증을 다시 수행하세요.
다섯 번째 단계: 실제 출시 작업으로 배포 승인을 결정합니다
자동 게시를 켜기 전에 격리된 실제 출시 작업을 한 번 끝까지 실행합니다. 소스 커밋에서 최종 다운로드 파일까지 연결되는지 확인하고, 서명 검사·제출 기록·처리 결과·티켓 확인·설치 또는 실행 결과가 같은 배포 건을 가리키는지 점검합니다.
운영 기준은 증거에 따라 선택합니다.
- 모든 검사와 실제 실행이 통과하고 기록이 연결되면 자동 게시를 검토합니다.
- 검증은 통과하지만 승인이나 계정 권한이 아직 수동이라면 사람의 확인 단계를 유지합니다.
- 제출 결과와 최종 파일이 연결되지 않거나 서명·티켓 검증이 실패하면 배포를 멈추고 원인을 복구합니다.
| 관찰된 상태 | 다음 선택 | 배포 전 조치 |
|---|---|---|
| 제출 결과와 최종 파일 검증이 모두 확인됨 | 자동 게시 검토 | 실행 로그와 소스 커밋 연결 확인 |
| 제출은 완료됐지만 설치·실행 검증이 빠짐 | 수동 승인 유지 | 격리 환경에서 최종 패키지 시험 |
| 서명, 티켓 또는 제출 대상이 일치하지 않음 | 배포 중단 | 산출물 재생성 또는 서명·제출 흐름 수정 |
| 운영 항목 | 원격 Mac CI에서 확인할 내용 | 보류 또는 복구 조건 |
|---|---|---|
| 자격 증명 | 접근 권한, 주입 경로, 교체 책임 | 로그나 저장소에 비밀이 노출됨 |
| 실패 재시도 | 같은 제출 대상인지 새 산출물인지 구분 | 원인을 모른 채 같은 작업을 반복함 |
| 로그 보존 | 커밋, 파일 식별 정보, 제출 기록 연결 | 최종 파일을 추적할 수 없음 |
| 복구 책임 | 서명·공증·게시 담당자와 승인 경계 | 실패 시 담당자나 롤백 절차가 정해지지 않음 |
| 결정 조건 | 선택 | 적용 기준 |
|---|---|---|
| Xcode, 서명 자산, 비밀 관리, 최종 설치 검증이 원격 노드에서 재현됨 | 원격 Mac CI로 출시 작업 운영 | 실패 로그와 책임자를 함께 관리 |
| 도구는 실행되지만 실제 배포 검증 또는 승인 경로가 미완성 | 수동 승인과 격리 시험 유지 | 자동 게시를 보류하고 빠진 증거를 보완 |
| 필요한 서명 자산이나 Mac 실행 환경을 확보할 수 없음 | 게시를 멈추고 환경부터 마련 | 미검증 산출물을 배포하지 않음 |
마지막 선택: 현재 실행 환경의 한계와 Mac 운영 방식을 비교합니다
개발자 개인 Mac을 빌드 노드로 겸용하면 다른 작업과 CI가 자원을 두고 경쟁할 수 있고, 노드가 꺼져 있거나 계정 세션이 달라지면 작업이 멈출 수 있습니다. Linux 서버만으로는 macOS 전용 빌드와 서명 흐름을 대체할 수 없으며, 임시 가상 환경은 실제 출시용 서명과 배포 경로를 검증하는 조건이 맞지 않을 수 있습니다. 다만 지속적인 고정 부하나 물리 인터페이스 접근이 중요하다면 원격 임대보다 직접 보유한 Mac이 더 적합할 수 있습니다.
우선 실제 출시 작업으로 필요한 Xcode, 서명 자산, CI 제어 방식이 원격 Mac에서 충족되는지 확인하세요. 현재 쓸 수 있는 Mac 실행 환경이 없다면 NodeMini의 Mac 임대 안내에서 원격 환경을 검토할 수 있습니다. 운영 위치가 중요한 경우에는 서울 Mac mini 임대 안내도 비교해 보세요. 검증을 통과하기 전에는 자동 게시를 열지 말고, 제출부터 최종 배포 파일까지 이어지는 증거를 먼저 갖추는 편이 안전합니다.