⚡ Electron6. 패키징 & 배포Publishing & CDN — 다중 아키텍처·채널·delta

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: 91234890

Universal 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로
채널버전 형식대상
stable1.2.3일반 사용자
beta1.2.3-beta.4옵트인
canary/nightly1.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가 곧바로 동작한다. 하지만 일정 규모를 넘으면 세 가지 한계에 부딪힌다:

  1. 파일당 2GB 한도 — universal mac binary + nativeModule 포함 시 도달 가능.
  2. 세밀한 채널·롤백 불가 — release를 삭제해야 클라이언트가 다음 버전 강제 못 함.
  3. 분석/리포트 한계 — 누가 어느 버전을 받는지 측정이 어렵다.

그래서 Slack/Discord/Notion은 자체 업데이트 서버를 운영한다. Hazel(Vercel) 같은 오픈소스 프록시가 그 중간 단계 — GitHub Releases를 백엔드로 두고 자기 도메인에서 라우팅·메트릭스를 더한다. 1Password는 Tauri로 옮길 때도 자체 업데이트 인프라는 그대로 가져갔다.

Publishing은 기술 결정이 아니라 운영 성숙도의 지표다.


요약

  • 다중 아키텍처는 CI 매트릭스 + 사이닝 + 노타라이즈의 합성.
  • latest.yml이 클라이언트 dispatch table.
  • delta update(blockmap)는 대형 앱의 생명선.
  • GitHub Releases → 자체 서버는 성장의 통과의례.