Publishing & CDN — 다중 아키텍처·채널·delta
이 문서가 답하는 질문: “Apple Silicon·Intel·Windows arm64를 한 앱이 어떻게 동시에 지원하고, 사용자에게 적절한 빌드를 어떻게 라우팅하는가?” 한 줄 답 (Pyramid Top): “멀티 아키텍처 배포는 빌드 × 사이닝 × 노타라이즈 × 메타의 매트릭스다 —
latest.yml이 그 매트릭스를 클라이언트에게 알려주는 dispatch table 역할을 한다.”
Why — 왜 존재하는가
2020년 Apple Silicon 발표 이후 — 한 macOS 앱이 두 아키텍처를 지원해야 했다. Windows on ARM이 잡고 있고, Linux는 항상 다양했다. 하나의 binary로 끝나는 시대는 사라졌다.
| 분기 축 | 옵션 수 | 이유 |
|---|---|---|
| 운영체제 | 3 (mac/win/linux) | 표준 |
| 아키텍처 | 4+ (x64, arm64, ia32, armv7l) | Apple Silicon · Windows ARM · Raspberry Pi |
| 인스톨러 포맷 | 4+ (dmg/exe/AppImage/deb/snap) | 사용자 환경 |
| 채널 | 2~3 (stable/beta/canary) | 베타 사용자 분리 |
곱하면 — 한 릴리스에 15~30개 binary가 나온다. CI/CD가 생존 도구가 된다.
How — 어떻게 동작하는가
latest.yml이 매트릭스의 목차 — 클라이언트가 자기 OS/아키텍처에 맞는 entry를 찾아간다.
What — 구체 사양·수치·예시
CI 매트릭스 (GitHub Actions)
name: Release
on:
push:
tags: ['v*']
jobs:
build:
strategy:
matrix:
include:
- os: macos-14 # arm64 runner
arch: arm64
- os: macos-13 # x64 runner
arch: x64
- os: windows-latest
arch: x64
- os: windows-latest
arch: arm64
- os: ubuntu-latest
arch: x64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx electron-rebuild
- name: Build & sign
env:
CSC_LINK: ${{ secrets.MAC_CERTS }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: npx electron-builder --publish always --${{ matrix.arch }}latest.yml — 클라이언트 라우팅 메타
# latest.yml (Windows x64)
version: 1.2.0
files:
- url: my-app-Setup-1.2.0-x64.exe
sha512: PqOQrBe...
size: 75432109
blockMapSize: 81234
path: my-app-Setup-1.2.0-x64.exe
sha512: PqOQrBe...
releaseDate: '2026-05-19T10:30:00.000Z'# latest-mac.yml (macOS, universal 또는 분리)
version: 1.2.0
files:
- url: my-app-1.2.0-arm64.dmg
sha512: ...
size: 91234567
- url: my-app-1.2.0-x64.dmg
sha512: ...
size: 91234890Universal macOS Binary
// electron-builder.json
{
"mac": {
"target": [
{ "target": "dmg", "arch": ["x64", "arm64"] }
// 또는 universal
// { "target": "dmg", "arch": ["universal"] }
]
}
}universal vs split: universal은 한 dmg에 두 아키텍처 fat binary → 사용자 다운로드 2배. split은 분리된 dmg → autoUpdater가 자기 아키텍처를 골라 다운로드. 보통 split 선호 (트래픽·CDN 비용 절감).
Delta Update — blockmap
# v1.0.0 → v1.1.0
# Full: my-app-1.1.0.dmg (90MB)
# Diff: 75% 동일 → 22.5MB만 다운로드blockmap은 binary를 고정 크기 청크로 나눠 각 청크의 hash를 기록. 클라이언트가 새 blockmap을 받고 바뀐 청크만 다운로드한다.
효과: 대형 앱(VSCode 100MB+, Slack 200MB+)에서 업데이트 시 90% 트래픽 절감.
채널 분리
{
"publish": [
{ "provider": "github", "releaseType": "release" }
]
}// 사용자 옵트인 시
autoUpdater.channel = 'beta';
// 베타 빌드 시 package.json 버전
// "1.3.0-beta.1" → beta 채널의 latest로| 채널 | 버전 형식 | 대상 |
|---|---|---|
| stable | 1.2.3 | 일반 사용자 |
| beta | 1.2.3-beta.4 | 옵트인 |
| canary/nightly | 1.2.3-canary.20260519 | 개발자·internal |
CDN 선택
| 옵션 | 비용 | 한계 |
|---|---|---|
| GitHub Releases | 무료, 트래픽 한도 없음(?) | 1파일 2GB, public 권장 |
| S3 + CloudFront | 트래픽 비용 | 가장 안정적·관리 부담 |
| Hazel (Vercel) | 무료, GitHub 프록시 | Vercel 한도 |
| Nucleus | 자체 호스팅 | 인프라 직접 |
macOS Sparkle XML (electron-updater 외 옵션)
<?xml version="1.0"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
<channel>
<item>
<sparkle:version>1.2.0</sparkle:version>
<enclosure url="https://updates.mycompany.com/my-app-1.2.0.dmg"
sparkle:dsaSignature="..."
length="91234567"
type="application/octet-stream"/>
</item>
</channel>
</rss>What-if — 잘못 쓰면
- 함정 1: x64 빌드만 배포 → Apple Silicon 사용자가 Rosetta로 실행 → 느림 + 메모리 2배. 사용자 신고 폭주.
- 함정 2: blockmap 없이 빌드 → delta update 안 됨 → 매번 full 다운로드 → 트래픽 비용 폭증.
- 함정 3:
latest.yml을 수동 편집 → 클라이언트가 잘못된 binary 다운로드. 항상 빌드 도구가 생성하게 둘 것. - 함정 4: 비공개 GitHub Repo + Releases → 사용자 token 없으면 다운로드 못 함. public release만 auto-update 가능.
- 함정 5: 베타 채널을 디폴트에 노출 → 일반 사용자가 베타 받음 → 사고 신고 폭주. opt-in UI 필요.
- 함정 6: CDN 캐시 TTL 너무 김 → 새 버전 배포 후에도 사용자가 옛
latest.yml을 받음. TTL 5분 권장.
Insight — 흥미로운 이야기
“GitHub Releases는 무료지만 대규모 앱은 결국 떠난다”
작은 Electron 앱은 GitHub Releases로 시작한다 — 무료, 안정적, autoUpdater가 곧바로 동작한다. 하지만 일정 규모를 넘으면 세 가지 한계에 부딪힌다:
- 파일당 2GB 한도 — universal mac binary + nativeModule 포함 시 도달 가능.
- 세밀한 채널·롤백 불가 — release를 삭제해야 클라이언트가 다음 버전 강제 못 함.
- 분석/리포트 한계 — 누가 어느 버전을 받는지 측정이 어렵다.
그래서 Slack/Discord/Notion은 자체 업데이트 서버를 운영한다. Hazel(Vercel) 같은 오픈소스 프록시가 그 중간 단계 — GitHub Releases를 백엔드로 두고 자기 도메인에서 라우팅·메트릭스를 더한다. 1Password는 Tauri로 옮길 때도 자체 업데이트 인프라는 그대로 가져갔다.
Publishing은 기술 결정이 아니라 운영 성숙도의 지표다.
요약
- 다중 아키텍처는 CI 매트릭스 + 사이닝 + 노타라이즈의 합성.
latest.yml이 클라이언트 dispatch table.- delta update(blockmap)는 대형 앱의 생명선.
- GitHub Releases → 자체 서버는 성장의 통과의례.