Electron 완벽 가이드 | 크로스플랫폼 데스크톱 앱·IPC·Auto Update·실전 활용

Electron 완벽 가이드 | 크로스플랫폼 데스크톱 앱·IPC·Auto Update·실전 활용

이 글의 핵심

Electron으로 크로스플랫폼 데스크톱 앱을 개발하는 완벽 가이드입니다. Main/Renderer 프로세스, IPC, Auto Update, 배포까지 실전 예제로 정리했습니다.

실무 경험 공유: Electron으로 Windows, macOS, Linux 앱을 동시에 출시하면서, 개발 시간이 70% 단축되고 유지보수가 간편해진 경험을 공유합니다.

들어가며: “데스크톱 앱 개발이 어려워요”

실무 문제 시나리오

시나리오 1: 플랫폼마다 다시 개발해야 해요
Native 개발은 비용이 큽니다. Electron은 한 번 개발로 모든 플랫폼을 지원합니다.

시나리오 2: 웹 기술을 활용하고 싶어요
Native는 학습 곡선이 높습니다. Electron은 HTML, CSS, JavaScript를 사용합니다.

시나리오 3: 빠른 프로토타이핑이 필요해요
Native는 시간이 오래 걸립니다. Electron은 빠릅니다.


1. Electron이란?

핵심 특징

Electron은 웹 기술로 데스크톱 앱을 만드는 프레임워크입니다.

주요 장점:

  • 크로스플랫폼: Windows, macOS, Linux
  • 웹 기술: HTML, CSS, JavaScript
  • Node.js: 파일 시스템 접근
  • 자동 업데이트: 쉬운 배포
  • 대규모 생태계: VS Code, Slack, Discord

2. 설치 및 프로젝트 구조

설치

npm install -D electron

기본 구조

my-app/
├── main.js          # Main Process
├── preload.js       # Preload Script
├── renderer.html    # Renderer Process
└── package.json

package.json

{
  "name": "my-app",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^28.0.0"
  }
}

3. Main Process

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

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
    },
  });

  win.loadFile('renderer.html');

  // DevTools 열기
  win.webContents.openDevTools();
}

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

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

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

4. IPC (Inter-Process Communication)

Main → Renderer

// main.js
const { ipcMain } = require('electron');

ipcMain.handle('get-app-version', () => {
  return app.getVersion();
});

ipcMain.handle('read-file', async (event, filePath) => {
  const fs = require('fs').promises;
  return await fs.readFile(filePath, 'utf-8');
});

Preload Script

// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  getAppVersion: () => ipcRenderer.invoke('get-app-version'),
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
});

Renderer

<!-- renderer.html -->
<!DOCTYPE html>
<html>
  <body>
    <button id="btn">Get Version</button>
    <p id="version"></p>

    <script>
      document.getElementById('btn').addEventListener('click', async () => {
        const version = await window.electronAPI.getAppVersion();
        document.getElementById('version').textContent = version;
      });
    </script>
  </body>
</html>

5. React 통합

설치

npm install react react-dom
npm install -D @vitejs/plugin-react vite electron-builder

vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  base: './',
  build: {
    outDir: 'dist-react',
  },
});

main.js

const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });

  if (process.env.NODE_ENV === 'development') {
    win.loadURL('http://localhost:5173');
  } else {
    win.loadFile(path.join(__dirname, 'dist-react/index.html'));
  }
}

app.whenReady().then(createWindow);

React Component

import { useState, useEffect } from 'react';

export default function App() {
  const [version, setVersion] = useState('');

  useEffect(() => {
    window.electronAPI.getAppVersion().then(setVersion);
  }, []);

  return (
    <div>
      <h1>My Electron App</h1>
      <p>Version: {version}</p>
    </div>
  );
}

6. 파일 시스템

// main.js
const { ipcMain, dialog } = require('electron');
const fs = require('fs').promises;

ipcMain.handle('open-file', async () => {
  const { canceled, filePaths } = await dialog.showOpenDialog({
    properties: ['openFile'],
    filters: [{ name: 'Text Files', extensions: ['txt', 'md'] }],
  });

  if (canceled) return null;

  const content = await fs.readFile(filePaths[0], 'utf-8');
  return { path: filePaths[0], content };
});

ipcMain.handle('save-file', async (event, { path, content }) => {
  await fs.writeFile(path, content, 'utf-8');
  return true;
});

7. Auto Update

npm install electron-updater
// main.js
const { autoUpdater } = require('electron-updater');

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

  autoUpdater.checkForUpdatesAndNotify();
});

autoUpdater.on('update-available', () => {
  console.log('Update available');
});

autoUpdater.on('update-downloaded', () => {
  autoUpdater.quitAndInstall();
});

8. 빌드 및 배포

electron-builder

npm install -D electron-builder

package.json

{
  "scripts": {
    "build": "vite build",
    "dist": "electron-builder"
  },
  "build": {
    "appId": "com.example.myapp",
    "productName": "My App",
    "directories": {
      "output": "release"
    },
    "files": [
      "dist-react/**/*",
      "main.js",
      "preload.js"
    ],
    "win": {
      "target": "nsis"
    },
    "mac": {
      "target": "dmg"
    },
    "linux": {
      "target": "AppImage"
    }
  }
}

빌드

npm run build
npm run dist

정리 및 체크리스트

핵심 요약

  • Electron: 웹 기술로 데스크톱 앱
  • 크로스플랫폼: Windows, macOS, Linux
  • IPC: Main ↔ Renderer 통신
  • Node.js: 파일 시스템 접근
  • Auto Update: 자동 업데이트
  • React/Vue: 프레임워크 통합

구현 체크리스트

  • Electron 설치
  • Main Process 구현
  • Renderer 구현
  • IPC 통신 구현
  • React/Vue 통합
  • 파일 시스템 접근
  • Auto Update 설정
  • 빌드 및 배포

같이 보면 좋은 글

  • Tauri 완벽 가이드
  • React 완벽 가이드
  • Node.js 완벽 가이드

이 글에서 다루는 키워드

Electron, Desktop, Cross-platform, IPC, Node.js, TypeScript, React

자주 묻는 질문 (FAQ)

Q. Tauri와 비교하면 어떤가요?

A. Electron이 더 성숙하고 생태계가 큽니다. Tauri는 더 가볍고 빠릅니다.

Q. 번들 크기는 어떤가요?

A. 기본적으로 100MB+ 입니다. Chromium과 Node.js가 포함되기 때문입니다.

Q. 보안은 어떤가요?

A. contextIsolation과 nodeIntegration: false를 사용하면 안전합니다.

Q. 프로덕션에서 사용해도 되나요?

A. 네, VS Code, Slack, Discord 등이 Electron으로 만들어졌습니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3