본문으로 건너뛰기

주에서 렌더러로

주 프로세스에서 렌더러 프로세스로 메시지를 보낼 때는 메시지를 받을 렌더러를 지정해야 합니다. 메시지는 WebContents 인스턴스를 통해 렌더러 프로세스로 전송되어야 합니다. 이 WebContents 인스턴스에는 ipcRenderer.send와 동일한 방식으로 사용할 수 있는 send 메서드가 포함되어 있습니다.

이 패턴을 시연하기 위해 네이티브 운영 체제 메뉴에서 제어하는 ​​숫자 카운터를 구축해 보겠습니다.

이 데모에서는 주 프로세스, 렌더러 프로세스, 사전 로드 스크립트에 코드를 추가해야 합니다. 전체 코드는 피들에서 확인할 수 있으며, 각 파일을 개별적으로 설명할 것입니다.

피들에서 열기

1. webContents 모듈로 메시지 보내기​

이 데모에서는 먼저 일렉트론의 Menu 모듈을 사용하여 주 프로세스에서 커스텀 메뉴를 구축해야 합니다. Menu 모듈은 webContents.send API를 사용하여 주 프로세스에서 대상 렌더러로 IPC 메시지를 전송합니다.

main.js (주 프로세스)
javascript
const { app, BrowserWindow, Menu, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => mainWindow.webContents.send('update-counter', 1),
label: 'Increment',
},
{
click: () => mainWindow.webContents.send('update-counter', -1),
label: 'Decrement',
},
],
},
]);
Menu.setApplicationMenu(menu);
mainWindow.loadFile('index.html');
}
// ...
main.js (주 프로세스)
javascript
const { app, BrowserWindow, Menu, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => mainWindow.webContents.send('update-counter', 1),
label: 'Increment',
},
{
click: () => mainWindow.webContents.send('update-counter', -1),
label: 'Decrement',
},
],
},
]);
Menu.setApplicationMenu(menu);
mainWindow.loadFile('index.html');
}
// ...

click 처리기가 update-counter 채널을 통해 렌더러 프로세스에 메시지(1 또는 -1)를 전송한다는 점을 주목해야 합니다.

javascript
click: () => mainWindow.webContents.send('update-counter', -1);
javascript
click: () => mainWindow.webContents.send('update-counter', -1);
정보

다음 단계를 위해 index.html과 preload.js 진입점을 로드하고 있는지 확인하세요!

2. 사전 로드를 통해 ipcRenderer.on 노출하기​

앞의 '렌더러에서 주로' 예시와 마찬가지로 사전 로드 스크립트에서 contextBridge와 ipcRenderer 모듈을 사용하여 렌더러 프로세스에 IPC 기능을 노출합니다.

preload.js (사전 로드 스크립트)
javascript
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback),
});
preload.js (사전 로드 스크립트)
javascript
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback),
});

사전 로드 스크립트를 로드한 후, 렌더러 프로세스는 window.electronAPI.onUpdateCounter() 수신기 함수에 접근할 수 있어야 합니다.

보안 경고

보안상의 이유로 ipcRenderer.on API 전체를 직접 노출하지 않습니다. 일렉트론 API에 대한 렌더러의 접근을 최대한 제한하세요.

정보

이 간단한 예시에서는 contextBridge를 통해 노출하는 대신 사전 로드 스크립트에서 직접 ipcRenderer.on을 호출하고 있습니다.

preload.js (사전 로드 스크립트)
javascript
const { ipcRenderer } = require('electron');
window.addEventListener('DOMContentLoaded', () => {
const counter = document.getElementById('counter');
ipcRenderer.on('update-counter', (_event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
});
});
preload.js (사전 로드 스크립트)
javascript
const { ipcRenderer } = require('electron');
window.addEventListener('DOMContentLoaded', () => {
const counter = document.getElementById('counter');
ipcRenderer.on('update-counter', (_event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
});
});

그러나 이 접근 방식은 수신기가 렌더러 코드와 직접 상호 작용할 수 없습니다. 따라서 contextBridge를 통해 사전 로드 API를 노출하는 것과 비교할 때 유연성이 떨어집니다.

3. 렌더러 프로세스 UI 구축하기​

이 모든 것을 하나로 묶기 위해, 로드된 HTML 파일에 #counter 요소가 포함된 인터페이스를 만들어 값을 표시할 것입니다.

index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Menu Counter</title>
</head>
<body>
Current value: <strong id="counter">0</strong>
<script src="./renderer.js"></script>
</body>
</html>
index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Menu Counter</title>
</head>
<body>
Current value: <strong id="counter">0</strong>
<script src="./renderer.js"></script>
</body>
</html>

마지막으로 HTML 문서에서 값을 갱신해 보겠습니다. update-counter 이벤트를 실행할 때마다 #counter 요소의 값이 갱신되도록 DOM을 조작하는 코드를 추가하겠습니다.

renderer.js (렌더러 프로세스)
javascript
const counter = document.getElementById('counter');
window.electronAPI.onUpdateCounter((_event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
});
renderer.js (렌더러 프로세스)
javascript
const counter = document.getElementById('counter');
window.electronAPI.onUpdateCounter((_event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
});

위의 코드에서는 사전 로드 스크립트에서 노출된 window.electronAPI.onUpdateCounter 함수에 콜백을 전달하고 있습니다. 두 번째 value 매개변수는 네이티브 메뉴의 webContents.send 호출에서 전달한 1 또는 -1에 해당합니다.

선택 사항: 응답 반환하기​

'주에서 렌더러로' IPC에는 ipcRenderer.invoke에 해당하는 것이 없습니다. 대신 ipcRenderer.on 콜백 내에서 주 프로세스로 응답을 보낼 수 있습니다.

이전 예시의 코드를 약간 수정하여 이를 시연해 보겠습니다. 렌더러 프로세스에서 event 매개변수를 사용하여 counter-value 채널을 통해 주 프로세스에 응답을 보냅니다.

renderer.js (렌더러 프로세스)
javascript
const counter = document.getElementById('counter');
window.electronAPI.onUpdateCounter((event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
event.sender.send('counter-value', newValue);
});
renderer.js (렌더러 프로세스)
javascript
const counter = document.getElementById('counter');
window.electronAPI.onUpdateCounter((event, value) => {
const oldValue = Number(counter.innerText);
const newValue = oldValue + value;
counter.innerText = newValue;
event.sender.send('counter-value', newValue);
});

주 프로세스에서 counter-value 이벤트를 수신하고 적절하게 처리합니다.

main.js (주 프로세스)
javascript
// ...
ipcMain.on('counter-value', (_event, value) => {
console.log(value); // 노드 콘솔에 값을 출력합니다.
});
// ...
main.js (주 프로세스)
javascript
// ...
ipcMain.on('counter-value', (_event, value) => {
console.log(value); // 노드 콘솔에 값을 출력합니다.
});
// ...