Electron 간단 정리하기
최근 Electron 에 흥미가 생겨 이를 정리해보고자 한다.
Electron 은 JavaScript, HTML, CSS 로 데스크톱 애플리케이션을 만들 수있는 프레임 워크이다.
Electron 은 Node.JS 로 로컬 시스템에 접근하고, Chromium 으로 화면을 구성한다.
HTML 에서 JS 로, Electron API 를 호출하면, Node 에서 제공하는 API 로 Local System 에 접근하여, 작업을 처리하는 등이 가능해 진다.
또한, Chromium 으로 화면을 구성하기 때문에, 내부에서 파일로 가지고 있는 HTML 뿐만 아니라, 외부에서 동작하는 HTML 사이트를 가져와서 그리는 것 또한 가능하다.
Electron 은 2021년 1월 를 기준으로 10 버전 이 최신이다.
10버전을 기준으로 Node.JS 12.16 TLS 버전을 지원하고, Chromium 은 85 버전을 지원하고 있다.
때문에, 만약 Electron 기준으로만, 화면을 구성한다면, Browser Polyfill 걱정 없이 개발이 가능하다.
Electorn 이모저모
Electron 의 최대의 장점은 Desktop App 을 Javascript 만으로도 개발이 가능다는 점이다.
비슷한 예로 Javascript 로 Mobile App 개발이 가능한 React Native 가 있다.
Electorn 은 Node 기반이기 때문에 npm 을 이용해서, 방대한 Javascript 오픈소스를 사용할 수 있다.
배포 시에는, Dependency 가 있는 Node Module 을 포함 시킬 수 있다.
또한 웹으로 개발한 소스를 약간의 수정과정을 거치면 그대로 사용이 가능하며, 필요하다면, Javascript 개발자가 직접 Desktop App 개발을 시작하는 것도 어렵지 않다.
한 코드 베이스로 Window, Mac, Linux 프로그램 작업 및 배포가 가능하다.
하지만 단점도 무시할 수 없는데, 가장 큰 단점은 “hello would” 를 출력하는 간단한 앱이라도 거대한 Chromium, 여러 Node Moudle 등을 포함 하므로 100 MB 이상의 저장 공간이 필요하다.
또한 다른 Native Code 로 만든 프로그램에 비해 속도와 메모리 측면에서도 가볍지는 않다.
하지만 단점에 비해 장점이 더 크기 때문에, Electorn 으로 App 개발을 시작 해보는건 나쁘지 않다고 본다.
간단 예제
// package.json
{
"name": "electron-app",
"main": "main.js",
"scripts": {
"start": "electron ."
}
}// main.js
const { app, BrowserWindow } = require('electron')app.whenReady().then(() => {
const win = new BrowserWindow()win.loadFile('index.html')
// win.loadUrl('http://example.com')
})app.on('window-all-closed', () => {
app.quit()
})
- package.json 의 main 에 있는 파일이 시작점이다.
- Electorn app 이 준비되면 (app.whenReady), BrowserWindow 로 새창을 생성한다.
- BrowserWindow 에 노출시킬 url 혹은 html 파일을 세팅한다.
- 만약 Electorn app 의 창이 모두 종료되었을때 (‘window-all-closed’) app.quit() 으로 App 을 종료 시킨다.
Electorn 을 구성하는 Main Process 와 Renderer Process
Electorn 에서 가장 중요한 개념은 Main Process
와 Renderer Process
이다.
Electorn 은 마치 서버 와 브라우저로 구성되어 있다고 비유할 수 있다.
이때 Main Process 가 Node 서버 이며, Renderer Process 가 Chromium 브라우저 이다.
Main Process
Electron App 은 반드시 1개의 Main Process
를 가진다.
이 Main Process
에서 Node.js API 사용이 가능하며 File System 등에 자유롭게 접근이 가능하다.
Main Process
에서 BrowserWindow
로 Renderer Process
를 생성하고, 웹페이지로 GUI 를 구성하게 된다.
만약 Main Process
가 종료되면, Renderer Process
도 함께 종료된다.
const { app, BrowserWindow } = require('electron')const win = new BrowserWindow({
nodeIntegration: true,
})
win.loadFile('index.html')
Renderer Process
Main Process 에서 Renderer Process 는 0개 이상일 수 있다.
각각의 Renderer Process
들은 독립적으로 작동하기 때문에, 다른 Renderer Process
에게 영향을 주지 않는다. 때문에 한페이지가 죽더라도, 다른 페이지에서는 여전히 동작이 가능하다.
만약 Renderer Process
에서 Node.js API 에 접근하려면 nodeIntegration
설정하면 되며, DevTool 을 확인해보고 싶다면, webContents.openDevTools
를 호출하면 된다.
// main process
const win = new BrowserWindow({
nodeIntegration: true,
})
win.webContents.openDevTools()// renderer process
<p>
<script>
const os = require('os') document.write(`
arch: ${os.arch()},
platform: ${os.platform()},
type: ${os.type()},
uptime: ${os.uptime()},
hostname: ${os.hostname()},
release: ${os.release()}
`)
</script>
</p>
Render Process 파일 접근 하기
만약 Render Process
에서 LocalFile 에 접근해야 한다면, 어떻게 할 수 있을까?
일반적으로 브라우저 에서 <input type="file" />
파일에 접근한다면, C:\fakepath\filename
형태로 경로가 노출 되며 브라우저에서 LocalFile 에 직접 접근하는건 보안상 막고 있다.
(추후 LocalFileSystemAPI 가 나오면 달라질수 있다)
Electorn 으로 노출되는 WEB 의 경우 DOM API
에 표준 API 이외에 Electorn 이 제공하는 API 내용이 추가되어 있다.
만약 실제 Local Full Path 를 확인해 보고 싶다면, Event.target.files[0].path
에서 실제경로를 확인할 수 있다.
/// jsx
<input type="file" onChange={(event) => {
console.log(event.target.value)
//C:\fakepath\filename console.log(event?.target?.files[0].path)
// Users/000/dir/filename
}} />
혹은 Drag & Drop
을 이용한다면, 아래와 같이 작성해 볼 수 있다.
document.addEventListener('drop', (event) => {
event.preventDefault();
event.stopPropagation(); for (const file of event.dataTransfer.files) {
console.log(file.path)
// Users/000/dir/filename
}
});
이외에도 IPC 를 통해 브라우저를 거치지 않고, Main Process
에서 dialog.showOpenDialog()
를 통해 파일에 접근하고, 이를 Renderer Process
에게 알려줄 수 있다.
// main processipcMain.on("open-dialog-file", async event => {
const file = await dialog.showOpenDialog({
properties: ["openFile"]
}); if (file) {
event.sender.send('selected', file.filePaths[0]);
}});// renderer processwindow.ipcRenderer.on('selected', (event, path) => {
console.log('selected', path)
})
Main Process 와 Render Process 의 IPC 통신
IPC 는 프로세스 간 통신 (Inter-Process Communication, IPC) 를 뜻한다.
Process 사이에 서로 데이터를 주고받는 행위 또는 그에 대한 방법이나 경로를 뜻한다.
Renderer Process
는 메모리 관리 및 보안상 로컬 시스템에 접근하는 것보다, IPC 를 통해 Main 프로세스와 통신 하며 시스템에 접근하는 것이 권장된다.
Electron IPC 에는 ipcMain, ipcRenderer 가 있다.
ipcMain
은 Main Process
에서 Renderer Process
에게 데이터를 받거나 전달해주는 모듈이다.
ipcRenderer
은 Renderer Process
에서 Main Process
에게 데이터를 받거나 전달해주는 모듈이다.
(ipcMain, ipcRenderer).on 에서 구독중인 채널로 부터 EventEmitter 객체와 전달되는 값을 받는다.
A Process 에서 넘어온 EventEmitter 객체
로 다시 A Process 의 적절한 채널에게 event.sender.send
를 이용하여, 값을 전달할 수 있다.
// main processipcMain.on('message', async (event, ...args) => {
event.sender.send('reply', 'pong');
});// renderer processipcRenderer.on('reply', async (event, ...args) => {
console.log(args)
});
만약 Render Process
직접 전달 해야 한다면, webContents.send
로 전달할 수 있다.
// main processconst win = new BrowserWindow()
win.webContents.send('reply', 'send')// renderer processipcRenderer.on('reply', async (event, ...args) => {
console.log(args)
});
on
과 send
로 나눠진 형태 이외에 처리된 결과를 바로받는 형태도 존재한다.
// main processipcMain.handle('invokable-ipc', async (event, ...args) => {
const result = await //...
return result
})// renderer process(async function() {
const result = await ipcRenderer.invoke('invokable-ipc')
console.log(result)
})()
Remote 모듈
Render Process
에서 Electron 에서 제공하는 dialog, menu 등의 모듈을 사용하려면, IPC 통신이 이루어져야 하는데, Main Process
와 Renderer Process
에서 데이터를 주고받는 형태 IPC 를 간략하게 만든 remote module
이 존재한다.
remote 모듈
은 IPC 와는 조금은 다르게 동작하는데, Main Process
에서만 사용 했던 API 들을 Renderer Process
에서 직접 사용하도록 설정이 가능하게 한다.
// main processnew BrowserWindow({
webPreferences: {
enableRemoteModule: true
}
});// renderer processconst {dialog} = require('electron').remote
(async () => {
const file = await dialog.showOpenDialog({
properties: ["openFile"]
}); console.log(file?.filePaths[0])
})()
반대로 Main Process 에서 Renderer Process 에 접근 하려면, webContents.executeJavascript
메서드를 사용할 수 있다.
Electorn 와 React
Electorn 을 Javascript 로 구현하는것은 어렵지 않다.
하지만 화면을 순수 HTML 과 JS로 구현 하는건, 공수가 많이 드는 일이다.
React 나 Vue 같이 Javascript 로 화면을 제어할 수 있는 도구를 활용한다면, 쉽게 작업할 수 있을 것이다.
다음 글에서 해당 내용을 작성해보고자 한다.