[Electron] 튜토리얼 Part 5: 앱 패키징 및 배포하기

Electron으로 앱 개발을 마무리했다면, 이제 사용자에게 배포할 준비를 해야 합니다. Electron은 기본적으로 패키징 도구를 제공하지 않으므로, Electron Forge같은 외부 도구를 사용해 앱을 빌드하고 플랫폼별 배포 파일로 변환해야 합니다. 

Electron Forge 설정하기

Electron Forge는 복잡한 패키징 과정을 단순화해주는 도구로, 다양한 플랫폼에 맞는 배포 파일을 쉽게 생성할 수 있습니다.

터미널에서 다음 명령어로 Electron Forge CLI를 설치합니다

npm install --save-dev @electron-forge/cli

기존 Electron 프로젝트를 Forge 형식으로 변환합니다

npx electron-forge import

forge.config.js 파일을 생성하고 package.json 파일에 아래와 같이 새로운 스크립트가 추가됩니다.

// package.json
{
  "name": "my-note-app",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
  },
  "devDependencies": {
    "electron": "^23.1.3",
    "@electron-forge/cli": "^6.0.0"
  }
}

간단한 어플리케이션 만들기

// main.js
const { app, BrowserWindow, ipcMain, Notification } = require('electron');
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    title: '간단한 메모 앱',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.loadFile('index.html');

  // IPC 핸들러: 알림 표시
  ipcMain.handle('notify', (event, message) => {
    new Notification({
      title: '메모 저장',
      body: message || '메모가 저장되었습니다!'
    }).show();
  });
}

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('noteAPI', {
  sendNotification: (message) => ipcRenderer.invoke('notify', message)
});
// index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <title>간단한 메모 앱</title>
</head>
<body>
  <h1>간단한 메모 앱</h1>
  <textarea id="memo" placeholder="여기에 메모를 작성하세요"></textarea>
  <button id="btn">저장</button>

  <script src="index.js"></script>
</body>
</html>
// index.js

const button = document.getElementById("btn");
button.addEventListener("click", async () => {
  saveMemo();
});

async function saveMemo() {
  const memo = document.getElementById('memo').value.trim();
  if (memo) {
    await window.noteAPI.sendNotification(memo.slice(0, 30) + (memo.length > 30 ? '...' : ''));
  }
}

앱 패키징 및 배포 파일 생성

패키징 실행

electron-forge package 명령어를 내부적으로 실행하여, 애플리케이션 코드와 Electron 바이너리를 하나의 폴더에 묶습니다. 해당 프로젝트에 out 폴더에 생성합니다.

npm run package

배포 파일 생성

패키지 폴더를 바탕으로 각 플랫폼별(예: Windows, macOS) 배포 파일(설치 프로그램, .app 파일 등)을 생성합니다. 해당 프로젝트에 out/make 폴더에 생성합니다.

npm run make

Forge 설정 파일과 코드 서명

Electron Forge는 다양한 플랫폼에 맞는 배포 파일을 생성할 수 있도록 여러 메이커들을 미리 구성해 둡니다. 또한, 앱의 신뢰도를 높이기 위해 코드 서명이 필수입니다. 코드 서명은 앱이 신뢰할 수 있는 출처에서 왔음을 증명해 주며, 특히 자동 업데이트 기능을 사용할 때 요구됩니다.

// forge.config.js
module.exports = {
  packagerConfig: {
    icon: './assets/icon' // .icns (macOS) 또는 .ico (Windows) 파일 경로
  },
  // macOS 서명 옵션 설정
  osxSign: {
    identity: process.env.MACOS_CERT_ID || 'Developer ID Application: Your Name (TEAMID)'
  },
  // macOS 노타리제이션 옵션 (선택 사항)
  osxNotarize: {
    tool: 'notarytool',
    appleId: process.env.APPLE_ID,
    appleIdPassword: process.env.APPLE_PASSWORD,
    teamId: process.env.APPLE_TEAM_ID
  },
  // Windows Code Signing 옵션 설정
  win32metadata: {
    CompanyName: "Your Company Name", // 회사 이름
    FileDescription: "Your App Description", // 파일 설명
    OriginalFilename: "your-app.exe", // 원래 파일 이름
    ProductName: "Your App Name" // 앱 이름
  }
  makers: [
    {
      name: "@electron-forge/maker-squirrel",
      config: {
        name: "my_electron_app",
        setupIcon: "./assets/app-icon.ico", // 설치 아이콘 (선택 사항)
        certificateFile: process.env.WINDOWS_CERT_PATH || "path/to/your/certificate.pfx", // 인증서 파일
        certificatePassword: process.env.WINDOWS_CERT_PASSWORD || "your-cert-password" // 인증서 비밀번호
      }
    },
    {
      name: "@electron-forge/maker-zip",
      platforms: ["win32", "darwin"]
    },
    {
      name: '@electron-forge/maker-dmg',
      config: {
        format: 'ULFO'
      }
    }
  ]
}