프로세스 모델

niyu·2021년 8월 23일
1
post-thumbnail

Electron Docs를 기반으로 작성하였습니다.

프로세스 모델

일렉트론은 Chromium다중 프로세스 아키텍처를 상속받았다.

웹 브라우저는 엄청나게 복잡한 어플리케이션으로서 웹 콘텐츠를 표시하는 주요 기능 외에도 여러 창 관리하거나 third-party 익스텐션을 로드하는 것과 같은 많은 보조 기능들이 있다.

초기의 브라우저는 이 모든 기능에 대해 단일 프로세스를 사용했고, 만약 하나의 웹사이트가 중단되기라도 한다면 전체 브라우저에 영향을 미쳤다.

이 문제를 해결하기 위해 Chrome은 각 탭을 별도의 프로세스로 실행하여 한 탭에서 치명적인 오류가 발생해도 전체 어플리케이션이 중단되지 않도록 하였다.


Chrome의 다중 프로세스 아키텍처 / 그림출처: electronjs.org/docs/tutorial/process-model

일렉트론 어플리케이션은 Chrome의 자체 브라우저 및 렌더러 프로세스와 유사하게 구성된다.

일렉트론에는 크게 main 프로세스renderer 프로세스 두 가지 유형의 프로세스가 있다.


main 프로세스와 renderer 프로세스 다이어그램 / 그림출처: cameronnokes.com

Electron은 Chromium과 마찬가지로 프로세스 간 통신을 위해 IPC(Inter-Process Communication)를 사용한다. IPC를 이용해 renderer에서 main 프로세스 작업을 트리거할 수 있으며 그 반대의 경우도 가능하다.

Main 프로세스

각 Electron 앱에는 어플리케이션의 진입점 역할을 하는 단일 main 프로세스가 있다. main 프로세스는 Node.js 환경에서 실행되어 모든 Node.js API를 사용할 수 있다.

window 관리

main 프로세스의 주요 목적은 BrowserWindow 모듈을 사용해 window을 만들고 관리하는 것이다.

BrowserWindow의 각 인스턴스는 웹 페이지를 로드하는 window를 만든다. 또한 BrowserWindow 모듈은 창 최소화나 최대화 같은 다양한 사용자 이벤트에 대한 핸들러를 추가할 수도 있다. webContents 객체를 이용해 main 프로세스에서 해당 웹 컨텐츠와 상호 작용할 수 있다.

const { BrowserWindow } = require('electron');

const win = new BrowserWindow({ width: 800, height: 1500 });
win.loadURL('https://github.com');

const contents = win.webContents;
console.log(contents);

BrowserWindow 인스턴스가 제거되면 해당 renderer 프로세스도 종료된다.

어플리케이션 수명 주기

메인 프로세스는 app 모듈을 통해 어플리케이션의 수명 주기를 제어한다. app 모듈은 어플리케이션 종료나 정보 패널 표시와 같은 사용자 정의 동작을 위한 이벤트와 메서드를 제공한다.

// app api를 사용해 MacOS에서 열려 있는 창이 없는 경우 앱을 종료한다.
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit();
})

네이티브 API

일렉트론은 메뉴, 다이얼로그 및 트레이 아이콘과 같은 기본 데스크톱 기능을 제어하는 다양한 모듈을 제공한다. 일렉트론의 main 프로세스 모듈의 전체 목록은 API 문서에서 확인할 수 있다.

Renderer 프로세스

각 일렉트론 앱은 각 BrowserWindow에 대해 별도의 renderer 프로세스를 생성하고 renderer 프로세스는 웹 컨텐츠를 렌더링하는 역할을 한다.

renderer 프로세스에서 실행되는 코드는 웹 표준에 따라 동작해야 하기 때문에, 단일 브라우저 창의 모든 UI와 앱 기능은 웹에서 사용하는 것과 동일한 도구 및 패러다임으로 작성되어야 한다.

  • HTML 파일은 renderer 프로세스의 진입점이다.
  • UI 스타일은 CSS를 통해 추가된다.
  • 실행 가능한 JavaScript 코드는 <script> 요소를 통해 추가할 수 있다.

웹 브라우저와 달리 일렉트론의 renderer 프로세스는 Node API를 사용할 수 있지만, 최근 버전에서는 보안을 이유로 렌더러 프로세스에서 Node.js를 바로 사용할 수 없다.


main 프로세스와 renderer 프로세스 / 그림출처: jlord.us/essential-electron

Preload 스크립트

Preload 스크립트에는 웹 컨텐츠가 로드되기 전에 렌더러 프로세스에서 실행되는 코드가 포함되어 있다.

BrowserWindow 생성자의 webPreferences 옵션을 통해 main 프로세스에 preload 스크립트를 연결할 수 있다.

// main.js

const { BrowserWindow } = require('electron');

const win = new BrowserWindow({
  width: 800,
  height: 600,
  webPreferences: {
    preload: 'path/to/preload.js'
  }
});

preload 스크립트는 전역 window 인터페이스를 renderer와 공유한다.

컨텍스트 격리

preload 스크립트는 연결된 renderer와 전역 window를 공유하지만, contextIsolation 기본값 때문에 preload 스크립트에서 window로 변수를 직접 연결할 수 없다.

컨텍스트 격리(Context Isolation)는 preload 스크립트와 일렉트론의 내부 로직이 webContents에 로드된 웹 사이트에 별도의 컨텍스트에서 실행되도록 하는 기능이다. 이는 웹 사이트가 일렉트론 내부 또는 preload 스크립트에 접근하지 못하도록 하기 때문에 보안 목적으로 중요하다.

컨텍스트 격리를 통해 preload 스크립트 내 권한을 가진 API가 웹 컨텐츠 코드로 유출되지 않도록 한다.

const mainWindow = new BrowserWindow({
  webPreferences: {
    contextIsolation: true // Electron 12부터 기본적으로 활성화된다.
  }
})

즉, preload 스크립트가 접근할 수 있는 window 객체가 웹 사이트에서 접근할 수 있는 객체와 실제로 다른 객체이다. 예를 들어 preload 스크립트에서 window.hello = 'wave'를 설정했다 했을 때, 웹 사이트에서 hello에 접근하려고 하면 undefined가 나온다.

window.myAPI = {
  desktop: true
}
console.log(window.myAPI); //  undefined

contextBridge 모듈을 사용해 안전하게 UI에 필요한 항목을 표시할 수 있도록 한다.

const { contextBridge } = require('electron');

contextBridge.exposeInMainWorld('myAPI', {
  desktop: true
})
console.log(window.myAPI) // { desktop: true }

0개의 댓글