Programming Guide/Electron

Electron 시작하기

발효홍삼 2022. 12. 8. 00:29
728x90

Electron 공식문서를 번역한 내용을 참고하여 작성된 내용입니다. 

What is Electron?

Electron은 자바스크립트, HTML, CSS를 사용하여 데스크톱 애플리케이션을 만드는 프레임워크이며 네이티브 애플리케이션입니다.

Node.js와 OS의 이벤트 루프를 합쳐 네이티브 UI를 제어하며, Chromium을 사용해서 HTML, CSS, 자바스크립트를 이용해 애플리케이션을 만들 수 있도록 해줍니다. 그리고 Electron을 이용해 작성한 앱은 Mac, Windows, 리눅스용으로 패키지 할 수 있습니다.

Quick Start

이 가이드에서는 electron/electron-quick-start처럼 Electron에서  Hello world앱을 만드는 과정을 안내합니다.

이 튜토리얼이 끝나면 실행 중인 Cromium, Node.js 및 Electron 버전에 대한 정보가 포함된 웹 페이지를 표시하는 브라우저 창이 열립니다.

Prerequisites

Electron을 사용하려면 Node.js를 설치해야 합니다. 사용 가능한 최신 LTS 버전을 사용하는 것이 좋습니다.

플랫폼에 대해 사전 구축된 설치 프로그램을 사용하여 Node.js를 설치하십시오. 그렇지 않으면 다른 개발 도구에서 비호환성 문제가 발생할 수 있습니다.


Node.js가 올바르게 설치되었는지 확인하려면 터미널 클라이언트에 다음 명령을 입력합니다.

$node -v
$npm -v
참고: Electronic은 Node.js를 이진 파일에 포함하므로 코드를 실행하는 Node.js 버전은 시스템에서 실행 중인 버전과 관련이 없습니다.

 

Create your application

Scaffold the project

electron앱은 다른 Node.js 프로젝트와 같은 일반적인 구조를 따릅니다. 폴더를 만들고 npm 패키지를 초기화하는 것부터 시작합니다.

$ mkdir electron-tutorial && cd electron-tutorial
$ npm init

이 튜토리얼의 목적을 위해 따라야 할 몇 가지 규칙이 있습니다.

  • 진입점은 main.js여야 한다.
  • author와 description은 아무 값이나 될 수 있으나, app packaging을 위해서는 반드시 필요하다. 

package.json 파일은 다음과 같아야 합니다.

{
  "name": "electron-app",
  "version": "1.0.0",
  "description": "Hello World!",
  "main": "main.js",
  "author": "balhyo",
  "license": "MIT"
}

그런 다음 electron 패키지를 앱의 devDependencies에 설치합니다.

$ npm install --save-dev electron
참고: Electron을 설치하는 데 문제가 있는 경우 고급 설치 안내서를 참조하십시오.

마지막으로 Electron을 실행할 수 있습니다.

package.json config의 scripts필드에 다음과 같은 start 명령을 추가합니다.

{
  "scripts": {
    "start": "electron ."
  }
}

이 start명령을 사용하면 개발 모드에서 앱을 열 수 있습니다.

$ npm start
Note: 이 스크립트는 프로젝트의 루트 폴더에서 Electron을 실행하도록 알려줍니다. 이 단계에서 앱은 실행할 앱을 찾을 수 없다는 에러를 즉시 발생시킵니다.

Run the main process

Electron 응용 프로그램의 시작 지점은 main 스크립트입니다. 이 스크립트는 전체 Node.js 환경에서 실행되는 main process를 제어하며 앱의 lifecycle 제어, 네이티브 인터페이스 표시, 권한 있는 작업 수행, 렌더러 프로세스 관리를 담당합니다.

실행하는 동안 Electron은 앱의 app scaffolding단계에서 구성한 package.jsonconfig의 package.json main field에서 이 스크립트를 찾습니다.

main script를 초기화하려면 프로젝트의 루트 폴더에 main.js라는 main.js 빈 파일을 생성하십시오.

참고: 이 시점에서 start 스크립트를 다시 실행하면 앱에서 오류가 더 이상 발생하지 않습니다! 하지만 아직 main.js에 코드를 추가하지 않았기 때문에 아무것도 할 수 없습니다.

Create a web page

​우리의 응용 프로그램을 위한 창을 만들기 전에, 우리는 그 안에 로드될 콘텐츠를 만들어야 합니다.  Electron에서 각 창은 로컬 HTML 파일 또는 원격 URL에서 로드할 수 있는 웹 콘텐츠를 표시합니다.

프로젝트의 루트 폴더에 index.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 World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
  </body>
</html>

Opening your web page in a browser window

이제 웹 페이지가 생겼으니 응용 프로그램 창에 로드하십시오. 이렇게 하려면 두 개의 Electron 모듈이 필요합니다.

  • app module: 응용 프로그램의 이벤트 수명 주기를 제어하는 응용 프로그램 모듈입니다.
  • BrowserWindow module : 응용 프로그램 창을 만들고 관리하는 모듈.

기본 프로세스가 Node.js를 실행하므로 Node.js를 파일 상단에서 CommonJS 모듈로 가져올 수 있습니다.

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

그런 다음 index.html을 로드하는 createWindow() 함수를 새 BrowserWindow 인스턴스에 추가합니다.

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

  win.loadFile('index.html')
}

다음으로 이 createWindow( ) 함수를 호출하여 창을 엽니다.

 

Electron에서는 app 모듈의 ready이벤트가 발생한 후에만 브라우저 창을 만들 수 있습니다. 이 이벤트는 app.whenReady( ) API를 사용하여 기다리시면 됩니다. whenReady( )가 Promise를 resolve 시킨 후 createWindow( )를 호출합니다.

app.whenReady().then(() => {
  createWindow()
})
참고: 이때 Electron 응용 프로그램은 웹 페이지를 표시하는 창을 성공적으로 엽니다!

 

Manage your window's lifecycle

​이제 브라우저 창을 열 수 있지만 각 플랫폼에서 더 네이티브 하게 느끼도록 하려면 몇 가지 추가 boilerplate code가 필요합니다. 응용 프로그램 창은 OS마다 다르게 동작하며 Electron은 앱에서 이러한 규칙을 구현하는 책임을 개발자에게 전가한다.

일반적으로 process 글로벌의 platform속성을 사용하여 특정 운영 체제에 대한 코드를 실행할 수 있습니다.

Quit the app when all windows are closed (Windows & Linux)

윈도와 리눅스에서 모든 윈도를 종료하면 응용 프로그램이 완전히 종료된다.


이를 구현하려면 app 모듈의'window-all-closed'이벤트를 listen 하고, 사용자가 macOS(darwin)에 있지 않은 경우 app.quit()를 호출하십시오.

 

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

 

Open a window if none are open (macOS)

리눅스와 윈도 앱이 창을 열지 않으면 종료되는 반면, 일반적으로 macOS 앱은 창을 열지 않아도 계속 실행되며 사용할 수 있는 창이 없을 때 앱을 활성화하면 새로운 창이 열립니다.

이 기능을 구현하려면 app 모듈의 activate이벤트를 listen 하고 브라우저 창이 열려 있지 않으면 기존 createWindow() 메서드를 호출하십시오.

ready 이벤트 전에는 창을 만들 수 없으므로 앱이 초기화된 후에만 activate이벤트를 listen 해야 합니다. 기존 whenReady() 콜백 내에서 이벤트 listener를 연결하여 이 작업을 수행합니다.

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})
참고: 이 시점에서 윈도 컨트롤이 완전히 작동해야 합니다!

Access Node.js from the renderer with a preload script

이제 마지막으로 해야 할 일은 Electron의 버전 번호와 의존성을 웹 페이지에 출력하는 것입니다.

기본 프로세스에서 노드의 글로벌 process 개체를 통해 이 정보에 액세스 하는 것은 간단합니다. 그러나 기본 프로세스에서 DOM은 렌더러의 document콘텍스트에 접근할 수 없으므로 수정할 수 없습니다. 그들은 완전히 다른 과정에 있어요!

참고: 전자 프로세스에 대한 자세한 내용은 프로세스 모델 문서를 참조하십시오.


preload 스크립트를 렌더러에 부착하는 것이 유용한 방법입니다. preload스크립트는 렌더러 프로세스가 로드되기 전에 실행되며 렌더러 글로벌(예: window 및 document)과 Node.js 환경에 모두 액세스 할 수 있습니다.

다음과 같이 preload.js라는 새 스크립트를 생성합니다.

 

window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
  }
})

위의 코드는 Node.js의 process.versions object에 접근하고 replaceTexthelper 함수를 실행하려 버전 번호를 HTML 문서에 삽입합니다.

렌더러 프로세스에 이 스크립트를 연결하려면 preload 스크립트의 경로를 기존 BrowserWindow 생성자의 webPreferences.preload 옵션에 전달하십시오.

// include the Node.js 'path' module at the top of your file
const path = require('path')

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

  win.loadFile('index.html')
}
// ...

여기에 사용되는 두 가지 Node.js 개념이 있습니다.

  • __dirname 문자열은 현재 실행 중인 스크립트의 경로(이 경우 프로젝트의 루트 폴더)를 가리킵니다.
  • path.join API는 여러 경로 세그먼트를 함께 결합하여 모든 플랫폼에서 작동하는 결합된 경로 문자열을 생성합니다.

현재 실행 중인 JavaScript 파일에 상대적인 경로를 사용하여 상대적인 경로가 개발 모드와 패키지 된 모드 모두에서 작동할 수 있도록 합니다.

Add functionality to your web contents

이때 프로그램에 기능을 추가하는 방법이 궁금할 수 있습니다.

웹 내용과 상호 작용하려면 렌더러 프로세스에 스크립트를 추가해야 합니다. 렌더러는 정상적인 웹 환경에서 실행되므로 index.html 파일의 </body> 태그 바로 앞에 <script> 태그를 추가하여 원하는 임의의 스크립트를 포함할 수 있습니다.

<script src="./renderer.js"></script>

그런 다음 renderer.js에 포함된 코드는 webpack을 사용하여 코드를 번들로 묶고 최소화하거나 사용자 인터페이스를 관리하는 것과 같이 일반적인 프런트엔드 개발에 사용하는 것과 동일한 JavaScript API 및 도구를 사용할 수 있습니다.

 

위의 단계를 수행한 후 다음과 같이 완벽하게 작동하는 Electron 응용 프로그램이 있어야 합니다.

전체 코드는 아래에서 확인할 수 있습니다.

 

main.js

// main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

preload.js

// preload.js

// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
  }
})

index.html

<!--index.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 World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
  </body>
</html>

지금까지 수행한 모든 단계를 요약하면 다음과 같습니다.

  • Node.js 응용 프로그램을 bootstrapped 하고 Dependency로 Electron을 추가했습니다.
  • 우리는 앱을 제어하고 Node.js 환경에서 실행되는 메인 프로세스를 실행하는 main.js스크립트를 만들었습니다. 이 스크립트에서는 Electron app과 BrowserWindow모듈을 사용하여 별도의 프로세스(렌더러)로 웹 콘텐츠를 표시하는 브라우저 창을 만들었습니다.
  • 렌더러에서 특정 Node.js 기능에 접근하기 위해, 우리는 BrowserWindow 생성자에 preload스크립트를 첨부했습니다.

Package and distribute your application

새로 만든 앱을 배포하는 가장 빠른 방법은 Electron Forge를 사용하는 것입니다.

1. 앱의 개발 의존성으로 Electron Forge를 추가하고 import명령을 사용하여 Forge's scaffolding을 설정합니다.

```sh npm2yarn
npm install --save-dev @electron-forge/cli
npx electron-forge import

✔ Checking your system
✔ Initializing Git Repository
✔ Writing modified package.json file
✔ Installing dependencies
✔ Writing modified package.json file
✔ Fixing .gitignore

We have ATTEMPTED to convert your app to be in a format that electron-forge understands.

Thanks for using "electron-forge"!!!
```

2. Forge의 make명령을 사용하여 배포 가능한 제품을 만듭니다.

npm run make

> my-electron-app@1.0.0 make /my-electron-app
> electron-forge make

✔ Checking your system
✔ Resolving Forge Config
We need to package your application before we can make it
✔ Preparing to Package Application for arch: x64
✔ Preparing native dependencies
✔ Packaging Application
Making for the following targets: zip
✔ Making for target: zip - On platform: darwin - For arch: x64

Electron Forge는 패키지가 위치할 out폴더를 만듭니다.

// Example for macOS
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
 
728x90