Electron 28 최신 버전 사용기

기운찬곰·2024년 1월 8일
0

Electron

목록 보기
4/5

Overview

최근에 실무에서 일렉트론을 사용할 일이 생겼습니다. 음... 오랜만에 일렉트론 공식문서를 보니 많은 것이 바뀐 거 같더군요. 특히 preload 사전 스크립트 개념이 낯설었습니다.


Electron 이란

  • Electron은 JavaScript, HTML, CSS를 사용하여 데스크톱 애플리케이션을 구축하기 위한 프레임워크입니다.
  • Electron은 Chromium 과 Node.js를 바이너리에 내장함으로써 하나의 JavaScript 코드베이스를 유지하고 Windows, macOS 및 Linux에서 작동하는 크로스 플랫폼 앱을 만들 수 있도록 해줍니다.

일렉트론의 핵심은 브라우저 기능과 노드 기능을 동시에 사용할 수 있다는 점입니다. 브라우저 기능을 사용하기 때문에 화면 구성이 쉽습니다. HTML, CSS, JS를 사용하면 되죠. 리액트를 붙여서 사용하도 됩니다. 노드 기능을 사용하면 운영체제 기능에 접근할 수 있죠. 예를 들어, 파일 시스템이 이에 해당합니다.


기본 튜토리얼

튜토리얼 : https://www.electronjs.org/docs/latest/tutorial/tutorial-prerequisites

튜토리얼을 보면 최소한의 Electron 애플리케이션을 만들면서 어느정도 이해하기 쉽게 되어있습니다.

main.js

package.json에 정의한 main.js는 모든 일렉트론 애플리케이션의 진입점입니다.

이 스크립트는 Node.js 환경에서 실행되며 앱의 수명 주기 제어, 기본 인터페이스 표시, 권한 있는 작업 수행 및 렌더러 프로세스 관리(나중에 자세히 설명)를 담당하는 기본 프로세스를 제어합니다.

BrowserWindow

Electron의 각 창에는 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'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Hello from Electron renderer!</title>
  </head>
  <body>
    <h1>Hello from Electron renderer!</h1>
    <p>👋</p>
  </body>
</html>

이제 웹페이지가 있으므로 Electron BrowserWindow 에 로드할 수 있습니다 .

import { app, BrowserWindow } from "electron";

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })

  win.loadFile('index.html')
}

app.whenReady().then(() => {
  createWindow()
})
  • app 은 애플리케이션의 이벤트 수명 주기를 제어합니다.
  • 앱 창을 생성하고 관리하는 건 BrowserWindow 입니다.

참고로 ECMAScript 모듈 (ESM)은 Electron 28부터 Electron에서 지원된다고 합니다. 즉, require를 더 이상 사용하지 않아도 됩니다.


메인 프로세스와 렌더러 프로세스

참고 : https://www.electronjs.org/docs/latest/tutorial/process-model

일렉트론에서 가장 중요한게 이 프로세스 모델을 이해하는게 아닐까 싶습니다.

일렉트론은 main 및 renderer 라는 두 가지 유형의 프로세스를 제어합니다.

메인(기본) 프로세스

각 Electron 앱에는 애플리케이션의 진입점 역할을 하는 단일 기본 프로세스가 있습니다. 기본 프로세스는 Node.js 환경에서 실행됩니다. 즉 , Node.js API를 모두 모듈화하고 사용할 수 있습니다 .

기본 프로세스의 주요 목적은 BrowserWindow모듈을 사용하여 응용 프로그램 창을 만들고 관리하는 것입니다. 별도의 렌더러 프로세스에서 웹 페이지를 로드하는 애플리케이션 창을 만듭니다.

또한 메인 프로세스는 Electron app모듈을 통해 애플리케이션의 수명주기를 제어합니다.

렌더러 프로세스

각 Electron 앱은 열릴 때마다 (그리고 웹에 포함할 때마다) 별도의 렌더러 프로세스를 생성합니다 . 이름에서 알 수 있듯이 렌더러는 웹 콘텐츠 렌더링을 담당합니다 .

  • HTML 파일은 렌더러 프로세스의 진입점입니다.
  • UI 스타일은 CSS(Cascading Style Sheets)를 통해 추가됩니다.
  • 실행 가능한 JavaScript 코드는 <script>요소를 통해 추가될 수 있습니다.

여기서 중요한 점은 렌더러가 다른 Node.js API에 직접 액세스할 수 없다는 의미이기도 합니다.

또한, 렌더러에 NPM 모듈을 직접 포함하려면 웹에서 사용하는 것과 동일한 번들러 툴체인(예: webpack 또는 vite)을 사용해야 합니다


사전 로드 스크립트

이 시점에서 렌더러 프로세스 사용자 인터페이스가 Node.js 및 Electron의 기본 데스크톱 기능과 기본 프로세스에서만 액세스할 수 있는 기능과 어떻게 상호 작용할 수 있는지 궁금할 것입니다.

nodeIntegration

이전까지는 nodeIntegration 이라는 속성을 사용하면 이 둘을 구분할 필요가 없었던거 같습니다.

  mainWindow = new BrowserWindow({
  ...
    webPreferences: {
      backgroundThrottling: false,
      nodeIntegration: true,
      enableRemoteModule: true
    }
  })

참고 : https://www.electronjs.org/docs/latest/tutorial/security

하지만 일렉트론 버전 업이 되면서 더 이상 사용하지 말 것을 강력히 권장하고 있습니다. 보안상 이유에서 입니다.

어떠한 경우에도 Node.js 통합이 활성화된 상태에서 원격 코드를 로드하고 실행해서는 안 됩니다. nodeIntegration을 비활성화 해야 합니다.

대신 사전 로드 스크립트라는 개념이 생겼습니다.

preload.js

처음에는 이게 뭐야 싶은 생각에 넘어갔었는데, 공식 문서를 쭉 읽다보니 이게 요즘 일렉트론에서 핵심이더군요.

메인 프로세스와 렌더러 프로세스를 연결하려면 preload 라는 특수 스크립트를 사용해야 합니다.

여기서 중요한 점은 Electron의 메인 프로세스와 렌더러 프로세스는 서로 다른 책임을 갖고 있으며 서로 바꿔 사용할 수 없습니다. 이는 렌더러 프로세스에서 직접 Node.js API에 액세스하거나 기본 프로세스에서 HTML DOM(문서 개체 모델)에 액세스할 수 없음을 의미합니다.

참고 : https://www.electronjs.org/docs/latest/tutorial/tutorial-preload

사전 로드 스크립트는 웹페이지가 렌더러에 로드되기 전에 삽입되어 실행됩니다. 권한 있는 액세스가 필요한 기능을 렌더러에 추가하려면 contextBridge API를 통해 전역 객체(window)를 정의할 수 있습니다 .

예를 들어 preload.js에서 process.versions를 렌더러 프로세스에 노출하도록 해봅니다.

const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // we can also expose variables, not just functions
})

이 스크립트를 렌더러 프로세스에 연결하려면 해당 경로를 BrowserWindow 생성자의 webPreferences.preload 옵션에 전달하세요.

const { app, BrowserWindow } = require('electron')
const path = require('node:path')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.loadFile('index.html')
}

이 시점에서 렌더러는 전역에 액세스할 수 있게 됩니다.

const information = document.getElementById('info')
information.innerText = `This app is using Chrome (v${window.versions.chrome()}), Node.js (v${window.versions.node()}), and Electron (v${window.versions.electron()})`

정리해보면 렌더러 프로세스에서 노드 기능을 사용하고 싶으면 사전 로드 스크립트를 정의해야 한다는 것입니다.


프로세스 간 통신 IPC

참고 : https://www.electronjs.org/docs/latest/tutorial/ipc

사전 로드 스크립트를 알았으니 그 다음으로 중요한 프로세스 간 통신 IPC 을 알아보겠습니다. IPC(프로세스 간 통신)는 Electron에서 기능이 풍부한 데스크톱 애플리케이션을 구축하는 핵심 부분입니다.

메인 프로세스와 렌더러 프로세스는 Electron의 프로세스 모델에서 서로 다른 책임을 갖기 때문에 IPC는 UI에서 기본 API를 호출하거나 기본 메뉴에서 웹 콘텐츠의 변경 사항을 트리거하는 등 많은 일반적인 작업을 수행하는 유일한 방법입니다.

Electron에서 프로세스는 ipcMain및 ipcRenderer모듈을 사용하여 개발자가 정의한 "채널"을 통해 메시지를 전달하여 통신합니다.

패턴 1. 렌더러에서 메인으로 단방향

렌더러 프로세스에서 기본 프로세스로 단방향 IPC 메시지를 실행하려면 ipcRenderer.send API를 사용하여 수신되는 메시지를 보낼 수 있습니다. 그러면 메인 프로세스에서는 ipcMain.on을 통해 받을 수 있습니다.

뭔가 느낌이 이벤트 리스너 설정하고 이벤트 발생 시키고... 소켓 통신과 방식은 비슷하다고 볼 수 있겠네요.

패턴 2. 메인과 렌더러 양방향

...

패턴 3. 메인에서 렌더러로 단방향

...


마치면서

이번 기회에 일렉트론에 대해 제대로 알게 된 시간이 되었던 거 같습니다. 제가 예전에 일렉트론에 쓴 글이 있었는데 부족한 부분이 많이 보이더군요. (하하...)


참고자료

profile
배움을 좋아합니다. 새로운 것을 좋아합니다.

0개의 댓글