Electron을 제대로 쓰려면 프로세스 모델과 프로세스 사이의 통신(IPC), 그리고 그 위에 세워진 보안 모델을 이해해야 합니다. 이 장에서는 BrowserWindow, ipcMain · ipcRenderer, contextBridge, 컨텍스트 격리와 보안 점검 목록을 차례로 다룹니다.
프로세스 모델
Electron은 Chromium의 다중 프로세스 구조를 그대로 물려받았습니다.
- 메인 프로세스: 앱마다 하나이며 Node.js 환경에서 실행됩니다. 앱 수명 주기(
app), 창(BrowserWindow), 메뉴, 트레이, 대화 상자 같은 네이티브 기능을 다룹니다. - 렌더러 프로세스: 창(정확히는 각
webContents)마다 하나씩 생기며 웹 페이지를 실행합니다. 기본 설정에서는 Node.js에 접근할 수 없습니다. - 유틸리티 프로세스:
utilityProcess.fork()로 메인 프로세스에서 띄우는 Node.js 자식 프로세스입니다. 무거운 계산이나 충돌 위험이 있는 작업을 메인 프로세스에서 떼어 낼 때 씁니다.
메인 프로세스가 오래 걸리는 작업으로 막히면 모든 창이 멈춘 것처럼 보입니다. 무거운 작업은 비동기 API나 유틸리티 프로세스로 옮깁니다.
// main.js
const { utilityProcess } = require('electron')
const path = require('node:path')
const child = utilityProcess.fork(path.join(__dirname, 'worker.js'))
child.on('message', (result) => console.log('결과:', result))
child.postMessage({ numbers: [1, 2, 3] })
// worker.js
process.parentPort.on('message', (event) => {
const sum = event.data.numbers.reduce((a, b) => a + b, 0)
process.parentPort.postMessage(sum)
})
BrowserWindow
BrowserWindow는 창 하나와 그 안의 렌더러를 만듭니다. 크기, 제목 표시줄, 최소 크기 같은 창 옵션과 함께 webPreferences로 렌더러의 보안 설정을 정합니다.
const win = new BrowserWindow({
width: 1000,
height: 700,
minWidth: 600,
show: false, // 내용이 준비된 뒤에 보여 깜빡임을 줄인다
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 기본값
nodeIntegration: false, // 기본값
sandbox: true, // 기본값
},
})
win.once('ready-to-show', () => win.show())
win.loadFile('index.html')
contextIsolation, nodeIntegration: false, sandbox는 현재 Electron의 기본값입니다. 위처럼 적어 두면 누군가 실수로 바꾸는 것을 막는 문서 역할을 합니다.
IPC: 프로세스 사이의 통신
렌더러는 운영체제 기능을 직접 쓸 수 없으므로, 메인 프로세스에 IPC로 요청합니다. 가장 많이 쓰는 형태는 요청과 응답입니다. 메인 프로세스에서 ipcMain.handle로 처리기를 등록하고, 렌더러 쪽(프리로드)에서 ipcRenderer.invoke로 호출하면 Promise로 결과를 받습니다.
// main.js
const { ipcMain, dialog } = require('electron')
ipcMain.handle('dialog:openFile', async (event) => {
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] })
return canceled ? null : filePaths[0]
})
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
openFile: () => ipcRenderer.invoke('dialog:openFile'),
setTitle: (title) => ipcRenderer.send('set-title', title),
onUpdateCounter: (callback) => {
ipcRenderer.on('update-counter', (_event, value) => callback(value))
},
})
응답이 필요 없는 알림은 ipcRenderer.send와 ipcMain.on으로 보냅니다. 반대로 메인 프로세스가 렌더러에 먼저 알릴 때는 win.webContents.send를 쓰고, 프리로드에서 ipcRenderer.on으로 받아 콜백으로 넘깁니다.
// main.js
ipcMain.on('set-title', (event, title) => {
const win = BrowserWindow.fromWebContents(event.sender)
win.setTitle(title)
})
win.webContents.send('update-counter', 1)
// renderer.js
document.getElementById('open').addEventListener('click', async () => {
const filePath = await window.api.openFile()
if (filePath) document.getElementById('file').textContent = filePath
})
window.api.onUpdateCounter((value) => console.log('카운터:', value))
contextBridge와 컨텍스트 격리
컨텍스트 격리가 켜져 있으면 프리로드 스크립트와 웹 페이지는 서로 다른 JavaScript 세계에서 실행됩니다. 페이지의 스크립트가 프리로드의 변수나 Electron API를 몰래 바꿀 수 없다는 뜻입니다. 두 세계를 잇는 유일한 통로가 contextBridge.exposeInMainWorld이며, 이를 통해 넘긴 함수와 값만 페이지에서 보입니다.
이때 ipcRenderer 객체를 통째로 노출하면 페이지가 아무 채널로나 메시지를 보낼 수 있게 되어 격리의 의미가 사라집니다. 위 예제처럼 용도가 분명한 함수만 노출하고, 콜백에 event 객체를 그대로 넘기지 않습니다.
보안 점검 목록
Electron 공식 보안 점검 목록의 핵심은 다음과 같습니다.
- 렌더러에서
nodeIntegration을 켜지 않습니다. contextIsolation과 sandbox를 끄지 않습니다.- 원격 콘텐츠는 HTTPS로만 불러오고, 신뢰할 수 없는 콘텐츠에는 프리로드 기능을 주지 않습니다.
- 모든 페이지에 콘텐츠 보안 정책(CSP)을 지정합니다.
- IPC 메시지의 보낸 쪽(
event.senderFrame)과 인자를 검증합니다. - 새 창 열기와 다른 주소로의 이동을 제한합니다.
shell.openExternal에는 검증한 주소만 넘깁니다.- Electron을 지원되는 최신 버전으로 유지합니다.
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('https://example.com/')) shell.openExternal(url)
return { action: 'deny' }
})
win.webContents.on('will-navigate', (event, url) => {
if (!url.startsWith('file://')) event.preventDefault()
})
정리
- 메인 프로세스는 하나, 렌더러는 창마다 하나이며, 무거운 작업은 유틸리티 프로세스로 옮길 수 있습니다.
- 요청과 응답은
ipcMain.handle · ipcRenderer.invoke, 단방향 알림은 send · on, 메인에서 렌더러로는 webContents.send를 씁니다. - 컨텍스트 격리 아래에서
contextBridge로 좁은 API만 노출합니다. - 보안 점검 목록을 따라 Node.js 통합을 끄고 CSP, IPC 검증, 이동 제한을 적용합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.