ICEFiction — AI와 함께 쓰는 소설 집필 데스크톱 앱 2026.07.27

ICEFiction 개발기 ⑥: 공증은 통과했는데 dmg가 막혔다 (v0.9.0 · macOS 배포)

Apple 공증이 44분 만에 통과했다는 메시지를 받고 확인해 보니 dmg는 여전히 거부였다. 공증되는 건 앱이고 사람들이 내려받는 건 dmg다. 끝난 줄 알았던 지점이 끝이 아니었던 게 하루에 세 번이었다.

공증(notarization)이 끝났다는 메시지를 봤다. Apple 서버가 44분을 붙잡고 있다가 내놓은 결과였다.

status: Accepted

이제 됐다 싶어 확인 명령을 넣었다.

$ spctl -a -vvv -t open --context context:primary-signature ICEFiction-0.9.0.dmg
ICEFiction-0.9.0.dmg: rejected
source=no usable signature

거부. 공증을 통과했는데 거부다.

v0.9.0을 맥에 배포하면서 겪은 일이다. 끝났다고 생각한 지점이 끝이 아니었던 게 오늘 하루에만 세 번이었다.

1. 개발자 계정이 있어도 인증서는 저절로 안 생긴다

첫 빌드 로그에 이렇게 찍혔다.

signing … identityName=Apple Development: icenovel@naver.com

Apple Development. 개발용 인증서다. 배포용인 Developer ID Application이 아니다. 나는 유료 개발자 계정이 멀쩡히 있는데 왜 이게 잡혔을까 싶었다.

한 계정에서 나오는 인증서가 여러 종류이고, 성격이 다르다.

종류용도어떻게 생기나
Apple Development내 맥에서 개발·테스트Xcode가 첫 빌드 때 자동 발급
Developer ID Application외부 배포직접 요청해야만 발급

Xcode를 한 번이라도 썼다면 앞의 것은 저절로 생겨 있다. 뒤의 것은 Apple이 절대 자동으로 만들어 주지 않는다. 외부 배포용이니까. 계정은 멀쩡한데 인증서만 없는 상태였던 것이다.

빌드 도구는 키체인에 있는 서명 신원 중 하나를 알아서 고른다. Developer ID가 없으면 있는 걸 쓴다. 그래서 조용히 개발용 인증서로 서명된 dmg가 나왔다. 에러도 경고도 없이.

2. 앱과 dmg는 따로 공증된다

인증서를 발급받고 다시 빌드했다. 이번엔 제대로 서명됐다.

Authority=Developer ID Application: BONGKOOG SEO (979U77ZZX6)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
flags=0x10000(runtime)

공증도 통과했다. 그런데 맨 위의 그 결과가 나왔다. 앱을 따로 확인해 봤다.

$ spctl -a -vvv --type exec ICEFiction.app
ICEFiction.app: accepted
source=Notarized Developer ID

$ spctl -a -vvv -t open ICEFiction-0.9.0.dmg
ICEFiction-0.9.0.dmg: rejected
source=no usable signature

앱은 통과, dmg는 거부.

빌드 도구가 공증하는 대상은 .app이다. dmg는 그 앱을 담아 나르는 별개의 산출물이라 손대지 않는다. 그래서 알맹이는 공증돼 있는데 껍데기는 무서명인 상태가 된다.

배포 관점에서 이게 왜 문제냐면, 사람들이 내려받는 건 dmg이기 때문이다. 격리 딱지가 붙는 것도 dmg다. 앱이 아무리 깨끗해도 그 앱을 꺼내려면 dmg를 먼저 열어야 한다.

그래서 dmg도 서명하고, 따로 공증을 넣고, 티켓을 붙였다.

$ xcrun stapler validate ICEFiction-0.9.0.dmg
The staple and validate action worked!

$ spctl -a -vvv -t open ICEFiction-0.9.0.dmg
ICEFiction-0.9.0.dmg: accepted
source=Notarized Developer ID

여기서 하나 덧붙일 게 있다. 공증 소요 시간이 전혀 예측되지 않는다. 같은 날 같은 계정으로 잰 값이다.

  • 앱 첫 제출 — 44분
  • 직후 dmg 제출 — 약 5분
  • 이후 CI에서 앱+dmg 전체 — 4분

첫 제출이 유독 느렸다. In Progress가 30분 넘게 이어져도 실패 신호가 아니다. 조급해서 다시 제출하면 큐 뒤로 밀려 손해만 본다. 이걸 모르면 44분 동안 뭔가 잘못됐다고 착각하기 딱 좋다.

3. 초록불인데 끝나지 않는 CI

로컬이 정리됐으니 자동 빌드도 확인해 봤다. 그러다 어제 돌린 실행이 눈에 들어왔다.

dmg (macos-14, arm64)  →  completed success
dmg (macos-13, x64)    →  queued        ← 14시간 36분째

Intel 빌드가 14시간 넘게 대기 중이었다. 실패한 게 아니라 대기다.

원인은 러너 라벨이었다. macos-13이 GitHub의 러너 목록에서 사라졌다. 없는 라벨을 요구하면 작업은 실패하지 않고 배정을 기다린다. 없는 걸 기다리는 거라 영영 진행되지 않는다.

고약한 건 나머지 절반은 정상이라는 점이다. Apple Silicon 빌드는 성공했고 산출물도 나왔다. 그런데 워크플로 전체는 끝나지 않는다. 태그를 밀어 배포했다면 dmg는 만들어졌는데 릴리스는 완료 표시가 안 되는, 설명하기 애매한 상태가 됐을 것이다.

배포 대상이 Apple Silicon뿐이라 Intel은 뺐다. 다만 여기서도 함정이 하나 더 있었다. Intel 러너는 사라진 게 아니라 이름이 바뀐 것이었다. macos-15는 ARM64고, Intel은 macos-15-intel처럼 접미사가 붙는다. 나는 처음에 이걸 "Intel 러너가 퇴역했다"고 적어 뒀다가 문서를 확인하고 고쳤다.

4. 아티팩트는 배포가 아니다

마지막 하나. 태그를 밀면 서명·공증된 dmg가 나오게는 해 뒀는데, 그게 Actions 아티팩트로만 올라갔다.

아티팩트릴리스 에셋
보관90일 후 삭제영구
다운로드GitHub 로그인 필요링크만으로 누구나

배포용이 아니다. 결국 이번 v0.9.0도 내가 손으로 릴리스에 붙였다. "태그만 밀면 끝"이라고 문서에 써 놓고서 실제로는 수동 단계가 남아 있었던 셈이다. 릴리스 자동 첨부를 붙여서 그 구멍을 막았다.

조건을 두 개 걸었다. 태그를 밀었을 때, 그리고 서명이 실제로 된 경우에만. 인증서가 없으면 무서명 dmg가 나오는데 그게 공개 릴리스로 나가면 안 되니까.

오늘 배운 것

세 번 다 같은 모양이었다.

끝난 것처럼 보이는 지점과 실제로 끝난 지점이 다르다.

공증 성공 메시지는 앱에 대한 것이었고, 초록색 체크는 절반의 작업에 대한 것이었고, 빌드 산출물은 배포물이 아니었다. 셋 다 검증 명령을 직접 넣어 보기 전까지는 끝난 줄 알았다.

그래서 이제 앱과 dmg 양쪽에 대해 spctlstapler validate를 전부 돌려 본다. 네 줄이다. 자동 빌드에도 같은 검증을 넣고, 서명이 안 돼 있으면 그 자리에서 실패하도록 했다. 무서명 dmg가 조용히 배포되는 것보다 빌드가 시끄럽게 깨지는 편이 낫다.

v0.9.0에 들어간 것

포장 이야기만 잔뜩 했는데, 내용물도 있다.

  • 책 전체 검색 — 원고 여러 편을 한 번에 훑는다
  • AI가 폴더를 본다 — 작업 중인 책 폴더의 맥락을 AI가 참고한다
  • 본문 슬래시 명령 — 본문에서 /를 치면 명령 목록이 뜬다
  • macOS 네이티브 메뉴 — 기본 메뉴의 줌 단축키가 앱 자체 줌과 겹쳐 이중으로 확대되던 문제를 없앴다

Apple Silicon 전용, macOS 12.0 이상. 이제 내려받아 열면 경고 없이 실행된다. 우클릭 → 열기 같은 우회는 필요 없다.

ICEFiction — AI와 함께 쓰는 소설 집필 데스크톱 앱 프로젝트로