junyeokk
Blog
Electron·2025. 12. 16

Electron Forge Makers

Electron 앱을 개발하고 나면, 사용자에게 배포할 수 있는 설치 파일을 만들어야 한다. 문제는 각 운영체제마다 설치 파일 형식이 완전히 다르다는 것이다. macOS는 DMG, Windows는 exe 설치 프로그램, Linux는 deb이나 rpm 패키지를 기대한다. 각 형식마다 아이콘 포맷도 다르고, 서명 방식도 다르고, 설치/업데이트 동작도 다르다.

이걸 직접 구현하려면 각 플랫폼의 패키징 도구를 개별적으로 설정하고, 빌드 스크립트를 플랫폼별로 분기 처리해야 한다. Electron Forge의 Maker 시스템은 이 문제를 추상화해서, 통일된 인터페이스로 플랫폼별 설치 파일을 생성할 수 있게 해준다.


Maker란 무엇인가

Maker는 Electron Forge의 패키징 파이프라인에서 "packaged app → distributable installer" 변환을 담당하는 플러그인이다. Forge의 빌드 파이프라인은 크게 세 단계로 나뉜다:

  1. Package — 앱 코드 + Electron 바이너리를 하나의 폴더로 묶는다 (electron-packager 사용)
  2. Make — 패키지된 폴더를 플랫폼별 설치 파일로 변환한다 (Maker가 담당)
  3. Publish — 생성된 설치 파일을 배포 채널에 업로드한다

Make 단계에서 Maker가 실행된다. 각 Maker는 특정 플랫폼/형식에 특화되어 있고, forge.config.tsmakers 배열에 등록하는 방식으로 사용한다.

typescript
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
import { MakerDMG } from '@electron-forge/maker-dmg';
import { MakerDeb } from '@electron-forge/maker-deb';
import { MakerRpm } from '@electron-forge/maker-rpm';

const config: ForgeConfig = {
  makers: [
    new MakerSquirrel({ /* options */ }),
    new MakerDMG({ /* options */ }),
    new MakerDeb({}),
    new MakerRpm({}),
  ],
};

electron-forge make 명령을 실행하면 현재 플랫폼에 해당하는 Maker만 자동으로 실행된다. macOS에서 빌드하면 MakerDMG만, Windows에서 빌드하면 MakerSquirrel만 동작한다. 모든 Maker를 등록해 놓더라도 현재 OS에 맞는 것만 실행되므로 안전하다.


주요 Maker 종류

MakerSquirrel (Windows)

Windows용 설치 프로그램을 생성한다. 내부적으로 Squirrel.Windows 프레임워크를 사용하며, NSIS와 달리 자동 업데이트(auto-update)를 네이티브로 지원하는 것이 가장 큰 특징이다.

typescript
new MakerSquirrel({
  setupIcon: path.join(iconsDir, 'win/icon.ico'),
  name: 'my-app',
  // 코드 서명
  certificateFile: process.env.WINDOWS_PFX_FILE,
  certificatePassword: process.env.WINDOWS_PFX_PASSWORD,
})

Squirrel은 설치 시 %LOCALAPPDATA% 하위에 앱을 배치한다. 관리자 권한이 필요 없는 per-user 설치 방식이다. 설치 프로그램이 실행되면 사용자에게 별도 UI 없이 바로 설치되고 앱이 시작된다. 이 "설치 마법사 없는" 경험이 Squirrel의 철학이다.

자동 업데이트 흐름은 이렇다:

  1. 앱이 시작될 때 업데이트 서버에 최신 버전 확인 요청
  2. 새 버전이 있으면 백그라운드로 다운로드
  3. 다음 앱 재시작 시 자동으로 새 버전 적용

이 동작은 electron-squirrel-startup 패키지와 연동해서 처리한다 (설치/업데이트/제거 시 앱이 특수 인자와 함께 실행되는 이벤트를 핸들링).

주의할 점: Squirrel은 설치/업데이트/제거 시 앱을 특수 커맨드라인 인자(--squirrel-install, --squirrel-updated, --squirrel-uninstall 등)와 함께 실행한다. 이 이벤트를 처리하지 않으면 설치 시 앱이 비정상적으로 동작할 수 있다.

출력물: Setup.exe (설치 프로그램) + .nupkg (업데이트 패키지)

MakerDMG (macOS)

macOS의 표준 배포 형식인 DMG(Disk Image) 파일을 생성한다. 사용자가 DMG를 열면 앱 아이콘과 Applications 폴더 바로가기가 보이고, 드래그 앤 드롭으로 설치하는 익숙한 방식이다.

typescript
new MakerDMG({
  icon: path.join(iconsDir, 'mac/icon.icns'),
  format: 'ULFO',  // lzfse 압축 (macOS 10.11+)
  // 창 크기와 배경 이미지 커스터마이징
  contents: [
    { x: 130, y: 220, type: 'file', path: '' },  // 앱 위치
    { x: 410, y: 220, type: 'link', path: '/Applications' },  // Applications 링크 위치
  ],
  background: path.join(__dirname, 'assets/dmg-background.png'),
})

DMG는 자동 업데이트를 자체적으로 지원하지 않는다. macOS에서 자동 업데이트를 구현하려면 별도로 electron-updater나 Sparkle 프레임워크를 사용해야 한다.

코드 서명과 공증(Notarization): macOS 10.15(Catalina)부터 공증 없는 앱은 Gatekeeper가 차단한다. Forge의 packagerConfig에서 osxSignosxNotarize 옵션을 설정하면 Make 단계 전에 자동으로 서명과 공증이 처리된다.

typescript
packagerConfig: {
  osxSign: {},
  osxNotarize: {
    appleId: process.env.APPLE_ID,
    appleIdPassword: process.env.APPLE_ID_PASSWORD,
    teamId: 'YOUR_TEAM_ID',
  },
},

공증은 Apple 서버에 바이너리를 업로드하고 보안 검사를 받는 과정이기 때문에 빌드 시간이 수 분 늘어난다.

출력물: .dmg 파일

MakerDeb (Linux - Debian/Ubuntu)

Debian 계열 Linux(Ubuntu, Debian, Mint 등)에서 사용하는 .deb 패키지를 생성한다.

typescript
new MakerDeb({
  options: {
    maintainer: 'Your Name',
    homepage: 'https://yourapp.com',
    icon: path.join(iconsDir, 'linux/icon.png'),
    categories: ['Utility'],
    // .desktop 파일에 들어갈 정보
    genericName: 'Photo Booth',
    description: 'A kiosk photo booth application',
  },
})

dpkg -i package.deb 또는 더블클릭으로 설치한다. 시스템 패키지 매니저(apt)와 통합되어 의존성 관리가 가능하고, 데스크톱 엔트리(바로가기)가 자동으로 생성된다.

출력물: .deb 파일

MakerRpm (Linux - Red Hat/Fedora)

Red Hat 계열 Linux(Fedora, CentOS, RHEL 등)에서 사용하는 .rpm 패키지를 생성한다. 옵션 구조는 MakerDeb과 거의 동일하다.

typescript
new MakerRpm({
  options: {
    icon: path.join(iconsDir, 'linux/icon.png'),
    categories: ['Utility'],
  },
})

출력물: .rpm 파일

기타 Maker

Maker형식플랫폼특징
maker-zip.zip모든 플랫폼가장 단순한 형식, 설치 없이 실행
maker-flatpak.flatpakLinux샌드박스 환경, Flathub 배포
maker-snap.snapLinuxSnap Store 배포, 자동 업데이트
maker-wix.msiWindows엔터프라이즈 환경, Group Policy 배포
maker-pkg.pkgmacOS시스템 레벨 설치, MDM 배포

아이콘 포맷

각 플랫폼이 요구하는 아이콘 포맷이 다르다. 이걸 미리 준비하지 않으면 빌드가 실패하거나 기본 Electron 아이콘이 표시된다.

플랫폼포맷최소 크기비고
Windows.ico256×256여러 크기 내장 (16, 32, 48, 256)
macOS.icns512×512 (1024 권장)iconutil.iconset에서 변환
Linux.png512×512단일 PNG 사용

실제 프로젝트에서는 하나의 고해상도 PNG(1024×1024)를 원본으로 두고, 빌드 스크립트나 도구(electron-icon-builder, png2icons 등)로 각 포맷을 자동 생성하는 것이 일반적이다.

아이콘 경로를 설정할 때 주의할 점이 있다. packagerConfig.icon에는 확장자 없이 경로를 지정해야 한다. Electron Packager가 현재 플랫폼에 맞는 확장자를 자동으로 붙인다.

typescript
packagerConfig: {
  // ✅ 확장자 없이
  icon: path.join(iconsDir, 'win/icon'),
  // ❌ 확장자 포함하면 다른 플랫폼에서 실패
  // icon: path.join(iconsDir, 'win/icon.ico'),
}

반면 각 Maker의 아이콘 옵션에는 해당 플랫폼 전용 확장자를 명시한다.


플랫폼별 빌드 전략

Electron Forge는 기본적으로 현재 OS에서 해당 OS의 설치 파일만 생성할 수 있다. Windows에서 DMG를 만들거나, macOS에서 exe를 만드는 크로스 컴파일은 직접적으로 지원되지 않는다.

이 제약 때문에 실제 프로덕션에서는 CI/CD 파이프라인에서 플랫폼별로 빌드를 분리한다.

yaml
# GitHub Actions 예시
jobs:
  build:
    strategy:
      matrix:
        os: [macos-latest, windows-latest, ubuntu-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm install
      - run: npx electron-forge make
      - uses: actions/upload-artifact@v4
        with:
          name: build-${{ matrix.os }}
          path: out/make/**/*

각 OS runner에서 electron-forge make를 실행하면 해당 플랫폼의 Maker만 동작한다. 결과물을 artifact로 수집하면 하나의 워크플로우에서 세 플랫폼의 설치 파일을 모두 얻을 수 있다.

macOS 빌드에서는 코드 서명과 공증을 위한 인증서/비밀 키를 CI 환경 변수로 주입해야 한다. GitHub Actions의 경우 secrets에 APPLE_ID, APPLE_ID_PASSWORD, 인증서 base64 등을 저장하고 빌드 시 복원하는 패턴이 일반적이다.


Maker 선택 기준

어떤 Maker를 쓸지는 배포 시나리오에 따라 달라진다.

자동 업데이트가 필요한 경우:

  • Windows: MakerSquirrel (네이티브 자동 업데이트)
  • macOS: MakerDMG + electron-updater 또는 MakerZip + Sparkle
  • Linux: MakerSnap (Snap Store 자동 업데이트)

엔터프라이즈 배포(IT 관리자가 설치):

  • Windows: MakerWix (MSI, Group Policy 배포 가능)
  • macOS: MakerPkg (MDM 배포 가능)
  • Linux: MakerDeb/MakerRpm (시스템 패키지 매니저 통합)

가장 빠르게 테스트용 배포:

  • 모든 플랫폼: MakerZip (설치 없이 압축 풀고 실행)

ASAR 패키징과의 관계

packagerConfig.asar: true 설정은 Package 단계에서 앱 코드를 .asar 아카이브로 묶는다. Maker는 이 ASAR 파일이 포함된 패키지 폴더를 입력으로 받아 설치 파일을 생성한다.

ASAR를 사용하면 두 가지 이점이 있다:

  1. 파일 시스템 보호 — 소스 코드가 단일 아카이브에 들어가서 일반 사용자가 직접 열어보기 어렵다 (완전한 보안은 아니지만 캐주얼한 접근을 방지)
  2. Windows 경로 길이 제한 우회node_modules의 깊은 중첩이 Windows의 260자 경로 제한에 걸리는 문제를 해결
typescript
packagerConfig: {
  asar: true,
  // 특정 파일을 ASAR 밖에 두어야 할 때
  // (네이티브 모듈, 대용량 리소스 등)
  extraResource: ['./native-addon.node'],
}

실전 팁

1. 출력 경로 확인

electron-forge make 실행 후 결과물은 out/make/ 디렉토리에 생성된다. 구조는 out/make/{maker-name}/{platform}/{arch}/ 형태다.

text
out/
└── make/
    ├── squirrel.windows/
    │   └── x64/
    │       ├── my-app-1.0.0-Setup.exe
    │       └── my-app-1.0.0-full.nupkg
    ├── deb/
    │   └── x64/
    │       └── my-app_1.0.0_amd64.deb
    └── dmg/
        └── x64/
            └── my-app-1.0.0.dmg

2. 특정 Maker만 실행

기본적으로 현재 플랫폼에 해당하는 모든 Maker가 실행된다. 특정 Maker만 실행하고 싶으면 --targets 플래그를 사용한다.

bash
# DMG만 생성
npx electron-forge make --targets @electron-forge/maker-dmg

# 여러 Maker 지정
npx electron-forge make --targets @electron-forge/maker-dmg,@electron-forge/maker-zip

3. 디버깅

빌드가 실패하면 DEBUG 환경 변수로 상세 로그를 확인할 수 있다.

bash
DEBUG=electron-forge:* npx electron-forge make

4. package.json 필드 확인

Maker들은 package.json의 필드를 참조해서 앱 이름, 버전, 설명 등을 설치 파일에 반영한다. name, version, description, author 필드가 제대로 설정되어 있는지 확인하자.

json
{
  "name": "my-app",
  "version": "1.0.0",
  "description": "My Electron App",
  "author": "Your Name"
}

name에 공백이나 특수문자가 들어가면 일부 Maker에서 문제가 발생할 수 있다. 소문자 kebab-case를 사용하는 것이 안전하다. 별도의 productName 필드로 사용자에게 보여줄 이름을 지정할 수 있다.


정리

  • Package → Make → Publish 파이프라인에서 Maker는 패키지된 앱을 플랫폼별 설치 파일로 변환하는 역할을 담당한다
  • 현재 OS에 맞는 Maker만 자동 실행되므로 모든 Maker를 등록해 두고 CI에서 matrix 빌드로 세 플랫폼을 커버하는 것이 표준 전략이다
  • 자동 업데이트가 필요하면 Windows는 Squirrel, macOS는 electron-updater, Linux는 Snap을 기준으로 선택한다

관련 문서