Wails 실전 강좌 #5 서명과 공증 — 배포된 앱이 신뢰받는 법
#4까지 노트 앱은 기능이 완성됐습니다. 이제 남의 기기에서 실행되게 만들 차례인데, 여기서 많은 개인 개발자가 막힙니다. 서명하지 않은 앱은 macOS와 Windows가 대놓고 경고를 띄워 실행을 막기 때문입니다. 이번 글은 배포된 앱이 신뢰받게 만드는 코드 서명과 공증을 다룹니다.
총 10편(2부 구성) 중 다섯 번째 글입니다.
- #1 · #2 · #3 · #4 트레이 상주와 전역 단축키
- #5 서명과 공증 — 배포된 앱이 신뢰받는 법 ← 이번 글
- #6 CI/CD 자동 릴리스 — GitHub Actions로 세 플랫폼 배포
왜 서명이 필요한가 #
입문 #6에서 빌드한 바이너리를 그대로 남에게 보내면, 받는 사람의 OS가 막아섭니다.
- macOS Gatekeeper: 서명·공증되지 않은 앱은 “확인되지 않은 개발자"라며 열기를 거부합니다. 사용자가 우클릭으로 강제 실행할 수는 있지만, 일반 사용자에게 그 과정을 안내하는 것은 배포로서 실패입니다.
- Windows SmartScreen: 서명되지 않았거나 평판이 쌓이지 않은 실행 파일에 “Windows가 PC를 보호했습니다” 파란 경고를 띄웁니다.
서명은 이 앱이 누가 만들었는지 보증하고, 공증은 애플이 악성 코드가 없는지 검사했다는 확인입니다. 둘을 거치면 경고가 사라지고, 배포 후 파일이 변조되지 않았음도 보장됩니다.
macOS: 서명 후 공증 #
macOS 배포는 두 단계입니다. 먼저 Apple Developer 인증서로 서명하고, 그다음 애플 서버에 올려 공증을 받습니다. 유료 Apple Developer Program 등록(연 $99)이 전제입니다.
codesign --deep --force --options runtime \
--sign "Developer ID Application: Your Name (TEAMID)" \
build/bin/노트.app--options runtime은 공증의 전제 조건인 하드닝드 런타임을 켭니다. 서명한 앱을 zip이나 dmg로 묶어 애플에 제출하고 공증을 기다립니다. 현재 도구는 notarytool입니다(과거 자료의 altool은 폐지됐습니다).
# 앱을 zip으로 묶어 제출 (완료까지 대기)
xcrun notarytool submit 노트.zip \
--apple-id "you@example.com" --team-id TEAMID \
--password "앱_전용_암호" --wait
# 공증 결과를 앱에 붙여 오프라인에서도 검증되게 한다
xcrun stapler staple build/bin/노트.app--wait는 공증이 끝날 때까지 기다립니다. 마지막의 stapler staple은 공증 티켓을 앱에 박아, 사용자가 인터넷 없이 실행해도 검증되게 합니다. 이 단계를 빠뜨리면 오프라인 사용자에게 경고가 다시 뜰 수 있습니다.
Windows: 인증서의 종류가 경험을 가른다 #
Windows 코드 서명은 인증서로 .exe나 설치 파일에 서명합니다. signtool로 서명하는 절차 자체는 단순하지만, 인증서의 종류가 사용자 경험을 크게 가릅니다.
| 인증서 | 대략 비용 | SmartScreen 경고 |
|---|---|---|
| OV(조직 검증) | 연 수십〜백 달러대 | 초기에 뜨다가 다운로드·평판이 쌓이면 사라짐 |
| EV(확장 검증) | 더 비쌈, HSM·토큰 필요 | 즉시 신뢰, 평판 축적 대기 없음 |
개인·소규모라면 OV로 시작해 평판이 쌓이길 기다리는 것이 현실적입니다. 즉시 경고를 없애야 하면 EV가 필요하지만 비용과 하드웨어 요건이 큽니다.
signtool sign /fd SHA256 /a `
/tr http://timestamp.digicert.com /td SHA256 `
build\bin\노트.exe/tr 타임스탬프는 반드시 넣습니다. 타임스탬프가 있으면 인증서가 나중에 만료돼도 서명 당시 유효했음이 보증되어, 서명이 계속 인정됩니다.
비밀은 로컬에 두지 않는다 #
서명에는 인증서·비밀번호·앱 전용 암호 같은 민감한 값이 필요합니다. 이것들을 코드나 저장소에 넣으면 그대로 유출입니다. 로컬에서 수동 서명할 때는 환경 변수나 OS 키체인에 두고, #6에서 CI로 넘길 때는 GitHub Secrets에 암호화해 저장합니다. 인증서 파일 자체도 base64로 인코딩해 Secret으로 넣고 워크플로에서 복원하는 방식이 표준입니다. 이 분리가 되어 있어야 다음 글의 자동화가 안전합니다.
정리 #
- 서명하지 않은 앱은 macOS Gatekeeper와 Windows SmartScreen이 경고로 막습니다. 서명은 제작자를 보증하고, 공증은 애플의 악성코드 검사 확인입니다.
- macOS는
codesign으로 하드닝드 런타임을 켜 서명한 뒤notarytool로 공증하고stapler staple로 티켓을 박습니다. 유지보수 중단된 gon 대신 이 방식이 현행입니다. - Windows는
signtool로 서명하되 타임스탬프를 반드시 넣습니다. OV는 평판이 쌓이면 경고가 사라지고, EV는 즉시 신뢰되지만 비쌉니다. - 인증서·비밀번호는 로컬에 두지 않고 환경 변수·키체인·GitHub Secrets에 둡니다. 이 분리가 다음 글의 CI 자동화 전제입니다.
- 다음 글에서 이 서명·공증을 GitHub Actions에 넣어, 태그 하나로 세 플랫폼 릴리스가 자동 생성되게 만듭니다.