Electron은 Main Process를 Nodejs 환경에서 수행하며, Electron Module, Nodejs 내장 기능 및 NPM으로 설치된 모든 패키지에 접근이 가능합니다.
반면에, 웹 페이지를 실행하는 Renderer Process는 보안상의 이유로 Nodejs를 실행하지 않습니다.
이 때문에, Electron에서 Main Process와 Renderer Process를 연결하려면 Preload Script를 사용해야합니다.
Preload Script는 HTML Dom & Nodejs env 모두에서 액세스할 수 있는 Context에서 실행되며, 웹 페이지가 Renderer에 의해 로드되기 전에 주입됩니다.
preload.js를 작성하여 Preload Script를 프로젝트에 적용하는 과정을 진행하겠습니다.
// Example
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
const nameOfInterface = "bridge";
contextBridge.exposeInMainWorld(nameOfInterface, {
// Set interface to use
ipcRenderer: {
sendMessage: (channel, args) => {
ipcRenderer.send(channel, args);
},
on: (channel, func) => {
const subscription = (_event, ...args) => func(...args);
ipcRenderer.on(channel, subscription);
return () => ipcRenderer.removeListener(channel, subscription);
},
once: (channel, func) => {
ipcRenderer.once(channel, (_event, ...args) => func(...args));
},
},
});
사전 정의된 interface의 기능과 관련된 스크립트를 preload.js에 작성합니다.
해당 기능은 추후 Renderer에서 window.bridge.emit() || window.bridge.on()과 같은 방식으로 호출할 수 있습니다.
// main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
win.loadFile('index.html')
}
preload.js를 main.js에 통합합니다.
// preload.d.ts
declare global {
interface Window {
bridge: {
ipcRenderer: {
sendMessage(channel: string, args: unknown[]): void;
on(
channel: string,
func: (...args: unknown[]) => void
): (() => void) | undefined;
once(channel: string, func: (...args: unknown[]) => void): void;
};
};
}
}
Renderer에 global object로 electron interface를 호출할 수 있도록 선언합니다.
// Renderer
// index.tsx
const interfaceCall = async () => {
await window.bridge.ipcRenderer.sendMessage("test", ["arg"]);
}
Renderer측의 interface bridge 연결작업은 이렇게 끝입니다. 다음은 main.js를 추가 작업하겠습니다.
// main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
const ipcMethodName = "" // preload.js에 선언된 interface bridge method 명을 사용합니다.
ipcMain.on(${ipcMethodName}, () => {
console.log("Running");
})
win.loadFile('index.html')
}
이렇게, Renderer와 Main Process간 Interface 통신 연결 튜토리얼을 정리하겠습니다.
References : Electron-Preload-Tutorial