출시·고도화 중
Electron 안내서 · 2/6
Electron 앱은 하나의 웹 페이지가 아니라 서로 다른 권한을 가진 여러 코드 조각으로 이루어집니다. 이 장에서는 메인 프로세스, 프리로드 스크립트, 렌더러가 각각 어느 파일에 들어가는지, package.json과 Forge 설정이 무엇을 정하는지 살펴봅니다.
| 구분 | 실행 위치 | 쓸 수 있는 것 | 대표 파일 |
|---|---|---|---|
| 메인 프로세스 | Node.js 환경, 앱마다 하나 | Electron 메인 API, Node.js 전체 | main.js |
| 프리로드 스크립트 | 렌더러 안, 페이지보다 먼저 | contextBridge, ipcRenderer 등 제한된 API | preload.js |
| 렌더러 | Chromium의 웹 페이지, 창마다 하나 | 웹 표준 API, 프리로드가 노출한 기능 | index.html, renderer.js |
메인 프로세스는 앱의 수명 주기와 창을 관리하고 파일 시스템 같은 운영체제 기능을 다룹니다. 렌더러는 브라우저 탭과 같아서 기본적으로 Node.js에 접근할 수 없습니다. 프리로드 스크립트는 두 세계 사이에 놓인 다리로, 렌더러에 꼭 필요한 기능만 골라 노출합니다.
앞 장의 프로젝트에 프리로드와 렌더러 스크립트를 더하면 다음과 같은 모양이 됩니다.
my-electron-app/
├── package.json # 이름, 버전, main, 스크립트, 의존성
├── main.js # 메인 프로세스
├── preload.js # 프리로드 스크립트
├── index.html # 렌더러 페이지
├── renderer.js # 렌더러에서 실행하는 스크립트
└── node_modules/프리로드 스크립트는 창을 만들 때 webPreferences.preload에 절대 경로로 지정합니다. 경로는 __dirname을 기준으로 만들어야 패키징한 뒤에도 올바르게 찾습니다.
const { app, BrowserWindow } = require('electron')
const path = require('node:path')
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
win.loadFile('index.html')
}
app.whenReady().then(createWindow)프리로드는 contextBridge로 렌더러의 window에 객체를 하나 붙입니다. 렌더러는 그 객체만 사용합니다.
// preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
})// renderer.js — index.html 에서 <script src="./renderer.js"></script> 로 불러온다
const info = document.getElementById('info')
info.innerText = `Chrome ${versions.chrome()}, Node.js ${versions.node()}, Electron ${versions.electron()}`vite-typescript 템플릿으로 만든 프로젝트는 소스를 src/에 두고, 메인 · 프리로드 · 렌더러를 Vite로 따로 빌드합니다.
my-app/
├── forge.config.ts # 패키징 · 설치 파일 · 플러그인 설정
├── forge.env.d.ts # Forge가 넣어 주는 전역 상수의 타입
├── index.html # 렌더러 페이지
├── package.json
├── src/
│ ├── main.ts # 메인 프로세스
│ ├── preload.ts # 프리로드 스크립트
│ ├── renderer.ts # 렌더러 진입점
│ └── index.css
├── tsconfig.json
├── vite.main.config.ts
├── vite.preload.config.ts
└── vite.renderer.config.ts개발 중에는 렌더러를 Vite 개발 서버에서 불러오고, 패키징한 앱에서는 빌드된 파일을 불러옵니다. 템플릿의 main.ts는 Forge가 정의하는 전역 상수로 이 둘을 구분합니다.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL)
} else {
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`))
}템플릿의 main.ts 맨 위에는 electron-squirrel-startup을 확인하는 코드가 있습니다. Windows 설치 프로그램(Squirrel)이 설치 · 제거 중에 앱을 실행할 때 바로 종료하게 하는 처리입니다.
Forge 프로젝트의 package.json에는 Forge 명령을 부르는 스크립트가 들어갑니다. main은 빌드된 메인 프로세스 파일(Vite 템플릿에서는 .vite/build/main.js)을 가리킵니다.
{
"main": ".vite/build/main.js",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish"
}
}forge.config.ts는 패키징 옵션(packagerConfig), 만들 설치 파일 형식(makers), 빌드 플러그인(plugins)을 정합니다.
import type { ForgeConfig } from '@electron-forge/shared-types'
import { MakerSquirrel } from '@electron-forge/maker-squirrel'
import { MakerZIP } from '@electron-forge/maker-zip'
import { MakerDeb } from '@electron-forge/maker-deb'
import { VitePlugin } from '@electron-forge/plugin-vite'
const config: ForgeConfig = {
packagerConfig: { asar: true },
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerDeb({})],
plugins: [
new VitePlugin({
build: [
{ entry: 'src/main.ts', config: 'vite.main.config.ts', target: 'main' },
{ entry: 'src/preload.ts', config: 'vite.preload.config.ts', target: 'preload' },
],
renderer: [{ name: 'main_window', config: 'vite.renderer.config.ts' }],
}),
],
}
export default configipcRenderer 전체를 노출하지 말고 이름이 분명한 함수만 노출합니다.__dirname이나 app.getAppPath()를 기준으로 만들어 패키징 후에도 동작하게 합니다.package.json의 main은 메인 프로세스 진입 파일을 가리킵니다.webPreferences.preload로 지정하고 contextBridge로 기능을 노출합니다.src/와 Vite 설정 파일, forge.config.ts로 빌드와 패키징을 관리합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.