-
Electron과 React간의 데이터 통신Archive/메모노트 데스크탑 앱 2022. 6. 29. 22:17
React에서 Electron의 API 사용
Electron을 이용해 생성한 데스크톱 앱은 일반적인 브라우저에서 동작하는 웹앱과 달리 강력한 기능들을 추가로 지원한다. 사용자의 컴퓨터 자원에 직접적으로 접근이 가능하기 때문에 파일 시스템을 이용하거나, 알림을 보낸다거나, 블루투스를 사용하는 식으로 여러 방식의 활용이 가능하다.
이때 이러한 기능들은 Electron 자체적인 API를 이용해 사용할 수 있는데, React를 이용해 Render를 진행한다면, React에서 어떻게 이러한 기능에 접근할 수 있을까?
이는 IPC 통신이라는 방식을 이용하여 가능하다.
IPC(Inter-Process Communication)이란, 프로세스 간 통신이라는 뜻으로, 프로세스들 사이에서 서로 데이터를 주고받는 방법이나 경로를 의미한다. Electron에서는 이를 Main 프로세스와 Renderer 프로세스 간에 통신을 위해 사용한다.
Main 프로세스와 Renderer 프로세스
여기서 말하는 Main 프로세스와 Renderer 프로세스에 대해 먼저 알아보자.
Main 프로세스는 앱의 라이프사이클을 관리하는 프로세스로, Electron 앱의 시작점이 되는 main.js에 해당하는 부분이라고 할 수 있다.
반면 Renderer 프로세스는 눈에 보이는 페이지를 만드는 프로세스로, React를 사용하여 Render를 진행할 경우, 해당 React 파일에 해당하는 부분이라고 할 수 있다.
이 두 프로세스는 서로 다른 영역이기 때문에, 두 프로세스 간에 통신을 위해서는 특정 통신 규약이 필요하다.
이를 위해 Electron에서는 IPC를 이용하고, Main 프로세스와 Renderer 프로세스는 각각 ipcMain, ipcRenderer라는 모듈을 사용해 통신을 진행한다.
ipcMain과 ipcRenderer
이 두 개의 모듈을 이용해 Main 프로세스와 Renderer 프로세스는 서로 통신을 주고받을 수 있다.
이때 통신을 진행할 channel을 정하여 특정 요청을 전송하면, 반대편에서는 이를 받아 사용하는 형태이다.
ipcRenderer
ipcRenderer는 Renderer 프로세스에서 Main 프로세스와 통신을 진행하기 위해 사용하는 모듈이다.
해당 모듈에서는 send와 on을 이용해 요청을 보내거나 받을 수 있다.
먼저 ipcRenderer.send()의 경우 다음과 같은 형태를 가진다. 이를 이용해 Main 프로세스로 데이터를 보낼 수 있다.
send(channel: string, ...args: any[]): void;
여기서 channel은 Main 프로세스로 보낼 채널명이고, args는 해당 채널명으로 보낼 데이터이다.
ipcRenderer.on()의 경우 다음과 같은 형태를 가진다. 이를 이용해 Main 프로세스에서 전송된 데이터를 받아서 처리할 수 있다.
on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this;
channel은 마찬가지로 채널명을 의미하고, listenr의 경우 요청을 받았을 때 동작시킬 함수를 의미한다.
이때 동작하는 함수의 경우 event와 args라는 인자를 가지게 되는데, args를 이용하여 반대편에서 보낸 데이터 내용을 확인할 수 있다.
ipcMain
ipcMain은 Main 프로세스에서 Renderer 프로세스와 통신을 진행하기 위해 사용하는 모듈이다.
해당 모듈에서는 on을 이용해 요청을 받을 수 있다.
ipcMain.on()의 경우 다음과 같은 형태를 가진다. 이를 이용해 Renderer 프로세스에서 전송된 데이터를 받아서 처리할 수 있다.
on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this;
만약 특정 데이터를 받았을 때, 다시 Renderer 프로세스로 답장을 보내고 싶을 때는 listner의 event 인자를 이용해여 reply를 진행할 수 있다. 이에 대한 예시는 아래와 같다.
const { ipcMain } = require("electron"); ipcMain.on("MESSAGE", (event, args) => { event.reply("REPLY", `reply: ${args}`); });
실제 프로젝트에 적용
해당 내용을 실제로 실습해보기 위해 React단에서 버튼을 만들고, 해당 버튼을 누르면 Electron API가 작동하도록 해보겠다.
이를 위해 React에서 Render한 버튼에 onClick 이벤트를 만들어, 버튼을 누르면 ipcRenderer를 이용해 Main 프로세스로 특정 신호를 주게 한다. 이후 Main 프로세스에서는 해당 신호를 받으면 Electron API를 실행하는 방식으로 진행한다.
ipc 통신을 위한 준비
ipc 통신을 사용하기 위해서는 약간의 준비가 필요하다.
const createWindow = () => { const win = new BrowserWindow({ width: 1200, height: 700, webPreferences: { enableRemoteModule: true, nodeIntegration: true, contextIsolation: false, }, }); win.loadURL("http://localhost:3000"); };
이전에 만들어 둔 main.js 파일에서 webPreferences라는 옵션을 추가한다.
여기서 저 3개의 옵션을 위와 같이 설정하면 된다. 각 옵션이 정확히 어떠한 역할을 하는지는 추후에 더 공부하여 정리하도록 하겠다.
React에서의 통신
Renderer 프로세스에 해당하는 React단에서는 통신을 위한 일종의 트리거를 만들기 위해, 다음과 같이 기존의 코드를 살짝 변경하였다.
import React from "react"; import logo from "./logo.svg"; import "./App.css"; function App() { function btnClick() { const electron = window.require("electron"); electron.ipcRenderer.send("BTN_CLICK", "message"); } return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p>Hello Electron!</p> <button onClick={btnClick}>Click it!</button> </header> </div> ); } export default App;
먼저 버튼을 하나 추가하여 onClick이라는 이벤트를 걸어두었다.
그리고 해당 버튼이 눌리게 되면 ipcRenderer.send()를 이용해 "message"라는 데이터를 Main 프로세스로 전송하도록 하였다. 이게 끝이다.
Electron에서의 통신
Main 프로세스에 해당하는 main.js 부분에서는 Renderer 프로세스에서 오는 데이터를 받기 위한 준비를 한다.
const { app, BrowserWindow, ipcMain, Notification } = require("electron"); app .whenReady() .then(createWindow) .then(() => { ipcMain.on("BTN_CLICK", (event, args) => { console.log(args); new Notification({ title: "NOTIFICATION", body: args }).show(); }); });
앱이 실행된 후부터 ipcMain.on을 이용해 "BTN_CLICK"이라는 이름으로 들어오는 모든 신호를 확인하도록 했다.
만약 신호가 도착하면 Notification이라는 Electron API를 이용하여 데스크톱에 직접적인 알림을 보내도록 하였다.
참고
https://kdydesign.github.io/2020/12/23/electron-ipc-communication/