Electron 에 React 적용해 보기

박성룡 ( Andrew park )
11 min readFeb 7, 2021

최근 Electron 에 흥미가 생겨 환경 세팅를 했던, 과정을 정리해보고자 한다.

어떠한 도구를 사용해도 상관없지만, 가장 쉽고, 개발하기 편한 React 를 Electorn 에서 사용해보고자 한다.

React 에 대한 고찰

현대의 웹은 복잡하고, 화려하다. 이는 단순 문서에서 Application 형태로 진화해가고 있기 때문이다.

순수 HTML, CSS, Javascript 으로 화면을 제어하는데, 공수가 많이 들기 시작하면서, 많은 개발자들이 화면과 DOM 을 제어할 수 있는 라이브러리와 프레임워크를 개발하였고, 이제 이런 도구들이 쓰는일이 어렵지 않게 되었다.

Electorn 은 내장된 Chromium 브라우저를 통해, Html, CSS, JS 만으로 GUI 를 구성할 수 있다. 때문에, 이를 순수 Html, CSS, JS로 개발해도 되지만, DOM 을 쉽게 조작할 수 있는 도구가 있다면, 개발 공수는 확연히 달라질 것이다.

React 는 DOM 을 매우 쉽게 조작할 수 있도록 도와준다. 완성된 React App 은 네트워크가 동작하지 않더라도 빌드된 html 과 js 만으로 완벽하게 단독으로 동작이 가능하다.

그렇다면 일렉트론에서는 React 를 어떻게 사용해야할까?

Electorn 에서 React 사용하기

Electorn 에서는 화면을 그리기 위해서는 html 파일 혹은 URL 이 필요하다.

React 는 Build 시 완성된 Html CSS, JS 를 생성해낸다.

Electorn 이 Production 상태 일때라면, Build 된 파일을 읽거나, 임의의 서버에 올린, React Page 을 노출시키면 된다.

하지만 개발 할때는 매번 수정사항이 생길때 마다 빌드해서 사용한다면, 빌드시간이 너무 오래 걸려 개발공수 시간이 오래 걸리게 된다.

React App 은 DevServer 를 통해 개발된 수정사항 만을 즉시 build 하고 화면에 적용시키는 Hot Reload 기능이 존재한다.

때문에, Develop Mode 에서는 DevServer 를 띄워, Election 과 URL 로 연결하여 개발을 진행하고, Production Mode 일때는 Build 한 파일을 Electorn 에 File 로 탑재한다면, 쉽게 개발하고, 배포할 수 있게 된다.

React 준비하기

React 를 준비하기 위해서는 보통 create-react-app 를 이용한다.

npx create-react-app 000

만약 해당방법이 싫거나, electron 을 먼저 세팅했다면,

npm i -D react react-do react-scripts 을 설치하고, pubilc/index.htmlsrc/index.js 를 준비한다.

중요한 점은 "homepage": "./" 를 세팅해주어야 한다.

이유인즉, ReactScript 에서 기본 경로를 / 로 잡기때문에, 빌드후에 경로를 못찾을 수 있기 때문이다.

<!-- pubilc/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="root"></div>
</body>
</html>
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return <div>hello world</div>
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
// package.json
{
...
"homepage": "./",
"scripts": {
...
"react:start": "react-scripts start"
},
"devDependencies": {
...
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "^4.0.1",
}
...
}

Electorn 준비하기

Election 을 준비하기 위해서는 pubilc/electron.js 파일과 package.json 파일이 필요하다.

main 에서 electron.js 을 entry file 로 정의하는 것이 중요하며, react 에서 사용하는 pubilc/index.html 과 함께 있는 것이 좋다.

// package.json
{
"name": "electron-app",
"main": "pubilc/electron.js",
"scripts": {
...
"electron:start": "electron ."
},
"devDependencies": {
...
"electron": "^11.1.1"
}
}
// main.js
const { app, BrowserWindow } = require('electron')
app.whenReady().then(() => {
const win = new BrowserWindow()
win.loadFile('http://localhost:3000')
})
app.on('window-all-closed', () => {
app.quit()
})

동시에 실행하기 concurrently 이해하기

Electorn 과 React 가 준비 되었다면 동시에 실행시킬 일만 남았다.

보통 스크립트를 동시에 실행시킬때는 && 를 많이 쓸 수 있다.

ex ) npm run react:start && npm run electron:start

하지만 React 에서는 해당 방법이 통하지 않는데, ReactScript 실행 후 종료되지 않고, 계속 변화를 watch 하고 있어, electron 이 실행되지 않는다.

때문에 다른 방법을 써야하는데 각각실행시켜도 되지만, 동시 실행을 지원해주는 도구가 존재한다.

concurrently 는 동시에 여러 명령을 실행할 수 있도록 도와준다.
ex ) concurrently "command1 arg" "command2 arg"

npm i -D concurrently 로 설치하고, 앞서 세팅한 React 와 Election 의 실행 Command 를 입력해 준다.
ex) ”concurrently \”npm run react:start\” \”npm run electron:start\””

// package.json
{
...
"scripts": {
"start": "concurrently \"npm run react:start\" \"npm run electron:start\"",
//...
},
"devDependencies": {
...
"concurrently": "^5.3.0",
}
...
}

wait-on 이해하기

Electorn 과 React 가 동시에 실행이 됬으나 이슈는 남아 있다.

Electorn 과 React 가 동시에 실행은 됬지만, Electron 화면에서는 아무것도 노출되지 않는다.

그 이유인즉, React Dev Server 가 올라오기전에 Electorn 에서 화면을 요청했기 때문에, 아무것도 노출되지 않는다.

때문에 매번 새로고침으로 다시 찾거나, React Dev Server 를 기다려줘야하는데, wait-on 을 이용하면 이를 쉽게 기다릴 수 있다.

npm i -D wait-on 으로 설치하고, electron script 에 ReactApp 이 뜰 때까지 기다리도록 wait-on [link] 을 추가해준다.

// package.json
{
...
"scripts": {
"start": "concurrently \"npm run react:start\" \"npm run electron:start\"",
"electron:start": "wait-on http://localhost:3000 && electron .",
...
},
"devDependencies": {
"wait-on": "^5.2.1"
}
...
}

실행해보면, React App 이 실행되고 나서, Electorn 이 그 다음으로 실행되어, 화면이 제대로 노출되는 것을 볼 수 있다.

브라우저 노출 막기

여기서 잠깐 귀찮은 것이 하나 있다.

React App 이 브라우저에서 열리고 난후, Electorn 에서 노출된다는 점이다.

매번 브라우저에서 React 화면이 노출되고 이를 다시 종료시키는건 여간 귀찮을 일이다.

우리는 Electorn 에 종속적인 화면을 만들 것 이므로 따로 브라우저를 노출 시킬 필요가 없다.

.env 파일은 ReactScript 에서 읽는 환경변수 파일이다.

여기에 브라우저에 노출되지 않도록 설정을 할 수 있다.

// .env
BROWSER=none

React App 에서 Electorn API 사용하기

이제 준비된 React App 에서, Electorn API 을 호출할 수 있을까?

Electron 과 React 는 별개의 환경 과 Thread 에서 동작한다.

때문에, Election 에서 React 를 띄우기 전에, Election API 사용 여부를 React App 에 알려주어야 한다.

이을 도와주는 기능이 preload 이다.

preload 는 BrowserWindow 로 노출되는 페이지에서, React Script 들이 실행되기 전에 load 될 Script 를 지정할 수 있다.

// pubilc/preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// pubilc/electron.js
let win = new BrowserWindow({
// ...
webPreferences: {
enableRemoteModule: true,
preload: __dirname + '/preload.js'
}
})

이로써 React App 과 Election 간의 ipc 통신이 가능해 졌다.

디버깅 하기

React App 에서 발생하는 Error 와 Log 를 볼고 싶을 수 있다.

Election 의 화면은 Chromium 으로 구성되어 있기 때문에, 크롬브라우저와 동일한 DevTools 를 볼 수 있다.

이를 보려면 Election 내에서 DevTools 활성화해주어야 한다.

// pubilc/electron.js
let win = new BrowserWindow()
win.webContents.openDevTools()

배포하기

Election 을 사용자들에게 배포하려면 운영체제 환경에 맞는 배포 방법을 세팅해야한다.

여러 도구가 있지만, electron-builder 를 사용하면 쉽게 적용할 수 있다.

npm i electron-builder -D 으로 electron-builder 를 설치했다면, 이제 package.json 에 Build 에 필요한 정보를 설정 해주어야, Application 에 정보들과 파일들이 세팅된다.

// package.json
{
...
"scripts": {
"react:build": "react-scripts build",
"build": "npm run react:build && electron-builder --publish=always"
...
},
"devDependencies": {
"electron-builder": "^22.9.1",
},
"build": {
"appId": "com.example.electron-app",
"files": [
"build/**/*",
"node_modules/**/*"
],
"directories": {
"buildResources": "assets"
}
},
...
}

빌드가 시작되면, React App 에서 /build 폴더에

  • 빌드된 Production File(html, js, css) 과
  • /pubilc 폴더에 있던, electron.jspreload.js

election-builder 에서 참조하게 된다.

election-builder 에서 는 entry file 이 build/electron.js 로 지정되어 있기 때문에, pubilc 폴더에 함께 넣어주는 것이 필요했다. ( 혹시 다른 방법이 있을 수 있지만 현재까지는 방법을 찾지 못하였다. )

Build 에 있는 React App File 들을, Election Build 과정에 넣게 된다.

한가지 특이사항으로 빌드된 html 을 electron 과 연결해주기 위해서는 pubilc/electron.js 에 한가지 세팅을 해줄 필요가 있다.

if (process.env.mode === 'dev') {
win.loadURL('http://localhost:3000')
} else {
win.loadURL(
`file://${path.join(__dirname, '../build/index.html')}`
)
win.loadFile(
`${path.join(__dirname, '../build/index.html')}`
)
}

loadURL 과 loadFile 어떤것을 사용해도 상관 없으나, production mode 에서는 build 된 html 파일을 경로로 잡아주어야 한다.

빌드가 성공한다면 /dist/OS 폴더 밑에 필요한 실행 파일이 존재하게 되며, asar 파일에 빌드된 정보가포함되어 있다.

결과적으로 완성된 코드는 다음과 같다.

--

--