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으로 만들어졌습니다.