๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
React

[Electron, React, TS] ๋ฐ์Šคํฌํ†ฑ ์•ฑ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

by LasBe 2024. 4. 1.
๋ฐ˜์‘ํ˜•

๐Ÿ“’ Electron + React ํ”„๋กœ์ ํŠธ ๊ตฌ์ถ•ํ•˜๊ธฐ


(24-04-01 ์ถ”๊ฐ€) electron-is-dev ๋ชจ๋“ˆ์ด ESM ๋ฐฉ์‹์„ ์ง€์›ํ•˜๊ธฐ ์‹œ์ž‘ํ•ด์„œ
CJS, require์—์„œ MJS, import ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋„๋ก ๊ธ€ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ Electron์ด๋ž€

Chromium๊ณผ Node.js๋ฅผ ํ•˜๋‚˜์˜ ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ†ตํ•ฉํ•˜์—ฌ

JS, HTML, CSS ๊ฐ™์€ ๊ธฐ๋ณธ์ ์ธ ์›น ์ง€์‹์œผ๋กœ๋„ ๋ฐ์Šคํฌํ†ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ž…๋‹ˆ๋‹ค.

 

์ด๊ฑธ๋กœ ๋ฐ์Šคํฌํ†ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์–ด๋–ป๊ฒŒ ๋งŒ๋“œ๋ƒ ์‹ถ๊ฒ ์ง€๋งŒ ์ด๋ฏธ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”

VSCode, Slack, Skype, Figma ๋“ฑ๋“ฑ๋“ฑ ์ด๋ฏธ ๊ฒ€์ฆ๋œ ์„œ๋น„์Šค๋“ค์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ ๋นŒ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ํ•˜๋‚˜์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์œˆ๋„์šฐ, ๋งฅ, ๋ฆฌ๋ˆ…์Šค์—์„œ

ํ”ํžˆ ๋ณด๋˜ ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ์˜ ํ˜•ํƒœ๋กœ ํŒจํ‚ค์ง•ํ•  ์ˆ˜ ์žˆ์–ด ๋ฏธ์นœ ์ƒ์‚ฐ์„ฑ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

  • ์ด ๊ธ€์—์„œ๋Š” ๋ฆฌ์•กํŠธ ๋„ค์ดํ‹ฐ๋ธŒ์˜ ์›น์•ฑ๊ณผ ๊ฐ™์ด React ์›น ํ”„๋กœ์ ํŠธ๋ฅผ Electron์œผ๋กœ ๊ฐ์‹ผ ํ˜•ํƒœ์˜ ํ”„๋กœ๊ทธ๋žจ์„ ์ œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ ‡๊ธฐ์— Electron ๊ด€๋ จ ์„ค์ •์„ ๊ฑฐ์˜ ๊ฑด๋“œ๋ฆด ํ•„์š”๊ฐ€ ์—†์–ด ๋Ÿฌ๋‹์ปค๋ธŒ๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ตœ์ข… ํŒจํ‚ค์ง€ ํŒŒ์ผ์—๋Š” ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฒˆ๋“ค๋งํ•œ ์‚ฐ์ถœ๋ฌผ ๊นŒ์ง€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ๋ณ„๋„์˜ ์›น์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ Node.js ๋ฐ yarn ์„ค์น˜ํ•˜๊ธฐ

Node.js๊ฐ€ ์„ค์น˜ ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด nvm ๋ฐ ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ์„ค์น˜ํ•ด์ฃผ์„ธ์š”.

$ npm i -g yarn

$ yarn -v // ๋ฒ„์ „์ด ํ‘œ์‹œ๋œ๋‹ค๋ฉด ์ •์ƒ ์„ค์น˜

์ €ํฌ๊ฐ€ ํŒจํ‚ค์ง•ํ•  ๋•Œ ์‚ฌ์šฉํ•  electron-builder๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ yarn ์‚ฌ์šฉ์„ ๊ฐ•๋ ฅ ๊ถŒ์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธ€๋กœ๋ฒŒ๋กœ ์„ค์น˜ํ•ด์ค๋‹ˆ๋‹ค.


๐Ÿ“Œ CRA๋กœ ์ดˆ๊ธฐ ํ”„๋กœ์ ํŠธ ์„ธํŒ…

$ npx create-react-app electron-app --template typescript

CRA๋ฅผ ์ด์šฉํ•ด ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.

 

์ €๋Š” TS๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…œํ”Œ๋ฆฟ์„ ์ ์šฉํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“Œ Electron ํŒจํ‚ค์ง€ ์„ค์น˜

$ npm i electron-is-dev
$ npm i -D electron electron-builder concurrently cross-env wait-on

์œ„์—์„œ ์„ค์น˜ํ•œ ํŒจํ‚ค์ง€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • electron-is-dev
    ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์„ ํ™•์ธ
  • electron
    ์ผ๋ ‰ํŠธ๋ก  ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.
  • electron-builder
    ์ผ๋ ‰ํŠธ๋ก ์„ ํŒจํ‚ค์ง•ํ•˜๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.
    ์ •๋ง ๋‹ค์–‘ํ•œ ์˜ต์…˜์ด ์žˆ์œผ๋‹ˆ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๊ผญ ์ฐพ์•„๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.
  • concurrently
    ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋ช…๋ น์–ด๋ฅผ ํ•˜๋‚˜์˜ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.
  • cross-env
    ์šด์˜์ฒด์ œ๋งˆ๋‹ค ๋‹ค๋ฅธ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •์„ ํ†ต์ผ์‹œ์ผœ์ค๋‹ˆ๋‹ค.
  • wait-on
    Node ํ™˜๊ฒฝ์—์„œ ํŒŒ๋‚˜ ์ด์ƒ์˜ ํฌํŠธ, ์†Œ์ผ“๊ฐ™์€ ์ž์›์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด์งˆ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๋„๋ก ์ง€์—ฐ์‹œํ‚ค๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

๐Ÿ“Œ Main Process ํŒŒ์ผ ์ƒ์„ฑ

Electron์—์„œ ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค ํŒŒ์ผ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ง„์ž…์ ์ด๋ฉฐ,

Electron API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

Node.js ๊ธฐ๋ฐ˜์—์„œ ๋Œ์•„๊ฐ€๋Š” ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// public/electron.js
const { app, BrowserWindow } = await import("electron");
const path = await import("path");
const isDev = await import("electron-is-dev");

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 900,
    height: 680,
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true,
      devTools: isDev,
    },
  });

  // ***์ค‘์š”***
  mainWindow.loadURL(
    isDev
      ? "http://localhost:3000"
      : `file://${path.join(__dirname, "../build/index.html")}`
  );

  if (isDev) mainWindow.webContents.openDevTools({ mode: "detach" });

  mainWindow.setResizable(true);
  mainWindow.on("closed", () => {
    mainWindow = null;
    app.quit();
  });
  mainWindow.focus();
}

app.on("ready", createWindow);

app.on("activate", () => {
  if (mainWindow === null) createWindow();
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});
  • ๊ฒฝ๋กœ: public ์•„๋ž˜์— ๋‘์–ด์•ผ ๋นŒ๋“œ ํ›„ build ํด๋” ๋ฐ”๋กœ ์•„๋ž˜์— electron.js ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์–ด ์ง„์ž…์  ์—ญํ• ์„ ํ•˜๊ธฐ ์ˆ˜์›”ํ•ด์ง‘๋‹ˆ๋‹ค.
  • ํŒŒ์ผ๋ช…: electron-builder๊ฐ€ ์ธ์‹ํ•˜๋Š” default ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค ํŒŒ์ผ ์ด๋ฆ„์ด electron.js ์ž…๋‹ˆ๋‹ค.

๋ณ„๋„ ์„ค์ • ํ•˜๊ณ ์‹ถ์ง€ ์•Š์œผ๋ฉด ํŒŒ์ผ ์œ„์น˜์™€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ๊ฐ€๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

 

์ฝ”๋“œ ๋‚ด ์ค‘์š”ํ‘œ์‹œํ•œ ๋ถ€๋ถ„์€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ผ ๊ฒฝ์šฐ์—๋Š” ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ณ ,

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์ผ ๊ฒฝ์šฐ์—๋Š” ๋นŒ๋“œ ํŒŒ์ผ์„ ์ฐพ์•„ ์‹คํ–‰์‹œํ‚จ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

 

์ด๋Ÿฌํ•œ ์ด์œ  ๋•Œ๋ฌธ์— ํŒจํ‚ค์ง•ํ•œ ํŒŒ์ผ์€ ๋ณ„๋„์˜ ์›น ์„œ๋ฒ„ ์—†์ด ์ž์ฒด์ ์œผ๋กœ ๊ตฌ๋™์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“Œ package.json ์„ค์ •

"type": "module",
"main": "public/electron.js",
"homepage": ".",

"scripts": {
    "react-start": "react-scripts start",
    "react-build": "react-scripts build",
    "start": "concurrently \"cross-env NODE_ENV=development BROWSER=none yarn react-start\" \"wait-on http://localhost:3000 && electron .\"",
    "build": "yarn react-build && electron-builder",
    "build:win": "yarn react-build && electron-builder --win --x64"
},
  • type
    ํ”„๋กœ์ ํŠธ๋ฅผ mjs ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • main
    ์ผ๋ ‰ํŠธ๋ก ์˜ ์—”ํŠธ๋ฆฌ ํŒŒ์ผ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.
  • start
    ๊ฐœ๋ฐœ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ํ›„ ์ผ๋ ‰ํŠธ๋ก ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • build
    ๋จผ์ € ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•ด ๋ฒˆ๋“คํŒŒ์ผ์„ ์ƒ์„ฑํ•œ ํ›„ ๊ทธ ๋ฒˆ๋“คํŒŒ์ผ์„ ํฌํ•จํ•˜์—ฌ ์ผ๋ ‰ํŠธ๋ก ์œผ๋กœ ํŒจํ‚ค์ง•ํ•ฉ๋‹ˆ๋‹ค.
    ์˜ต์…˜ ์—†์ด electron-builder๊ฐ€ ์‹คํ–‰๋  ๊ฒฝ์šฐ ํ˜„์žฌ ๊ตฌ๋™ํ•˜๋Š” ํ™˜๊ฒฝ์— ๋งž์ถฐ์„œ ํŒจํ‚ค์ง•ํ•ฉ๋‹ˆ๋‹ค.
    ์œ„ ์œˆ๋„์šฐ ํŒจํ‚ค์ง•๊ณผ ๊ฐ™์ด ๋งŽ์€ ํ™˜๊ฒฝ์— ๋งž์ถ˜ ์˜ต์…˜๋“ค์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— electron-builder ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.

์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ์ด ๋๋‚œ ํ›„ $ npm start๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์‹คํ–‰ ์ดํ›„ ํ‰์†Œ ๋ฆฌ์•กํŠธ๋ฅผ ๋‹ค๋ฃจ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐœ๋ฐœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

ํšŒ์‚ฌ์—์„œ ๋ฐ์Šคํฌํ†ฑ ์•ฑ์„ ๋งŒ๋“ค ์ผ์ด ์žˆ์–ด ์‚ฌ๋‚ด์—์„œ ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณด๋Š”๋ฐ ํ•œ ๋‹ฌ์ด ์ง€๋‚œ ์ง€๊ธˆ ํฐ ์–ด๋ ค์›€ ์—†์ด ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

 

์›น ๊ธฐ์ˆ ๋กœ ๋ฐ์Šคํฌํ†ฑ ์•ฑ์„ ๋งŒ๋“œ๋Š”๊ฒŒ ๋‚˜๋ฆ„ ์žฌ๋ฏธ์žˆ์–ด์„œ ์›น ๊ฐœ๋ฐœ์„ ํ•ด๋ณด์‹  ๋ถ„์ด๋ผ๋ฉด ํ•œ๋ฒˆ ์ฏค ๋„์ „ํ•ด๋ด๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€


์˜คํ”ˆ ์ฑ„ํŒ