Electron ๊ฐ•์ขŒ
ย - Last update: 2021-01-27

Basic of Electron (Udemy ๊ฐ•์˜)

Basic of Electron

  • ํ”Œ๋žซํผ. ๋ฐ์Šคํฌํ†ฑ ์•ฑ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•จ.
  • ์›น ์•ฑ์€ ์ตœ๊ทผ๋“ค์–ด ๊ต‰์žฅํ•˜๊ฒŒ ๋ณต์žกํ•ด์กŒ๋‹ค. Electron์€ ์ด๋Ÿฌํ•œ ํŠน์„ฑ์„ ๋ฐ์Šคํฌํ†ฑ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ํ”Œ๋žซํผ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋จ.
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ๊ฐํ•ด์•ผํ•  ๋ถ€๋ถ„์€ ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์œ ์ €์˜ ํ•˜๋“œ๋””์Šคํฌ์— ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ. ์ด๋Ÿฌํ•œ ์ œ์•ฝ์„ ํ’€์–ด์ฃผ๋Š” ๊ฒƒ์ด Electron ์ด๋ผ๋Š” ํ”Œ๋žซํผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
    • ์‚ฌ์šฉ์ž์˜ ์ปดํ“จํ„ฐ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ์ ์ด ํ•ต์‹ฌ!!
  • Electron === Google Chrome (Mostly..)
    • Task Manager๋ฅผ ๋ณด๋ฉด Google Chrome Helper๋ผ๋Š” ๋†ˆ์ด ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ๊ฐ™์ด ์กด์žฌํ•จ. ์ด๊ฒƒ์„ ์ดํ•ดํ•˜๋ฉด Electron์„ ์ดํ•ดํ•˜๋Š” ๋ฐ์— ๋„์›€์ด ๋œ๋‹ค.
    • ์™œ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ฐ™์ด ๋„๋Š”๊ฐ€?
      • ๊ฐ™์ด ์ฃฝ์ง€ ์•Š๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด. ๋žœ๋”๋ง ์ด์Šˆ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด. Isolate ์‹œํ‚ค๊ธฐ ์œ„ํ•ด.
      • RendererProcess๊ฐ€ ํ•ต์‹ฌ์ž„. ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์›นํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆฐ๋‹ค.
      • IPC(Inter Process Communication)์„ ํ†ตํ•ด MainWindow, RendererProcess๋“ค์ด ํ†ต์‹ ํ•œ๋‹ค.
      • ์›น๋ธŒ๋ผ์šฐ์ €์™€ ๋‹ค๋ฅธ ์ ์€, ์›น๋ธŒ๋ผ์šฐ์ €์˜ ์ฃผ์†Œํ‘œ์‹œ์ค„๊ณผ ๊ฐ™์€ ์™„์ „ ๋ธŒ๋ผ์šฐ์ € ์ž์ฒด์˜ ๋‚ด์šฉ๊นŒ์ง€๋„ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ .
  • Electron์˜ ์—ญ์‚ฌ(?)
    • GitHub -> Electron -> Atom (์ง€๊ธˆ์€ VS Code๊ฐ€ ๋จน์—ˆ์ง€๋งŒ..)
    • GitHub๋Š” ์ฒ˜์Œ์— Atom์„ ์œ„ํ•ด Electron์„ ๋งŒ๋“ค์—ˆ๊ณ (Atom์€ JS ์œ ์ €๊ฐ€ ๋งŽ์•„ JS๋กœ ๋งŒ๋“ค์—ˆ๋‹ค ํ•จ), ์ด๊ฒƒ์ด ์“ธ๋งŒํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•ด์„œ Open Source๋กœ ๋งŒ๋“ค์—ˆ์Œ
    • ์ด๊ฒƒ์„ Slack, Discord, VS Code ๋“ฑ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

Handling Electron Project

  • Electron (Run at terminal, Overall Chrome Process๋กœ ์ƒ๊ฐ) -> Create MainWindow(BrowserWindow) -> index.html์„ ๋กœ๋”ฉ.
    • BrowserWindow๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋กœ์ง ๋˜ํ•œ js๋กœ ์ž‘์„ฑํ•œ๋‹ค๋Š” ์ ! (ํƒ€ ์–ธ์–ด ์‚ฌ์šฉ 0%)
  • nodemon์„ ์‚ฌ์šฉํ•˜๋ฉด ์ง€์†์ ์ธ F5 ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ์•ผ ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Œ. -> ์ž๋™ ์‹œ์ž‘์„ ํ•ด์ฃผ๋Š” ์„œ๋ฒ„ side์—์„œ๋Š” ์ด๋ฏธ ์ž์ฃผ ์“ฐ์ด๊ณ  ์žˆ๋Š” ๋…€์„์ž„
  • ์•„๋ž˜ ๋ช…๋ น์–ด๋Š”, src/js/index.js ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์ฝ”๋“œ๋ณ€ํ™”๊ฐ€ ๊ฐ์ง€ํ•˜๋ฉด ์žฌ์‹œ์ž‘์„ ํ•˜๋„๋ก ์„ค์ •.
"start": "npm-run-all --parallel watch:webpack watch:nodemon",
"watch:webpack": "webpack --config=webpack.config.js --mode=development --watch",
"watch:nodemon": "nodemon --exec \"electron .\" --watch \"./src/js/setup.js\""
  • Step by Step Guide
    • npm init
    • package.json ์ƒ์„ฑ
    • npm install --save electron
    • 2 ํŒŒ์ผ ์ƒ์„ฑ ํ•„์š”. init์„ ์œ„ํ•œ js ๊ณผ ๋ณด์—ฌ์ค„ index.html
    • init์„ ์œ„ํ•œ js๋Š” ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰๋œ๋‹ค. node๋ฅผ ํ†ตํ•ด.. ์—ฌ๊ธฐ์„œ๋Š” require๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
      • const electron = require('electron');
      • import๋Š” node์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›๋˜์ง„ ์•Š์Œ.
    • app ์€ overall process ๋ฅผ ์˜๋ฏธํ•จ.
    const { app } = require('electron');
    • app์€ event๋ฅผ ๋ฐฉ์ถœํ•˜๋ฉฐ, ์ด๊ฒƒ์€ app.on ์œผ๋กœ ์ฒญ์ทจ ๊ฐ€๋Šฅํ•จ
      • Event Driven Programming
    • app ๋งŒ ์‹คํ–‰ํ•ด์„œ๋Š” ์‚ฌ์‹ค ์•„๋ฌด๊ฒƒ๋„ ์ง„ํ–‰๋˜์ง€ ์•Š์Œ!
      • ์ดํ›„ BrowserWindow๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•จ, ๊ทธ๋ž˜์•ผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ณด์ด๊ฒŒ ๋œ๋‹ค.
      • app ์€ ์ฒ˜์Œ ๋กœ๋”ฉ์ด ๋๋‚˜๋ฉด ready ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. ๊ทธ ํ›„ mainWindow๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ธ Electron App์˜ ํŒจํ„ด.
      • app ์— ์ „์ฒด Main Process์˜ Life Cycle์ด ์—ฎ์—ฌ ์žˆ๋‹ค๊ณ  ๋ณด๋ฉด ๋จ.
    • BrowserWindow
      • new BrowserWindow() ๋งŒ ํ•ด๋„ ๋ฐ”๋กœ ๋ญ”๊ฐ€ ๋‚˜์˜จ๋‹ค.
      • ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋„ ๋ฐ”๋กœ ํŠ€์–ด๋‚˜์˜ค๊ฒŒ ๋จ ์›ํ•œ๋‹ค๋ฉด.
      • ๊ทธ๋Ÿฌ๋‚˜ ์ฃผ์†Œ ํ‘œ์‹œ์ค„ ๊ฐ™์€ ๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์—†๋‹ค.
      • ์œ„์—์„œ ๋ณธ app๊ณผ๋Š” ๋‹ค๋ฅธ ๋…€์„์ž„. ์™„์ „ํžˆ ๋‹ค๋ฅธ Process ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋จ
      • ์ฒ˜์Œ ๋งŒ๋“ค๊ธฐ๋งŒ ํ•˜๋ฉด html document๊ฐ€ load๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋นˆ ํ™”๋ฉด๋งŒ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.
        • ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” new BrowserWindow().loadURL('https://google.com') ์™€ ๊ฐ™์ด ํ•˜๋ฉด ๋จ
        • ๋ฌผ๋ก  local file๋„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๊ฐ€๋Šฅํ•˜๋ฉฐ ๋Œ€๋ถ€๋ถ„์˜ ELectron App์€ ๋กœ์ปฌ ์•ฑ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋กœ์ปฌ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ฌ ๊ฒƒ์ž„.
      • ํŠน์ˆ˜ํ•œ Node.js ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์—๋Š” ์ผ๋ฐ˜ ์›น ๊ฐœ๋ฐœ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ ๊ฐœ๋ฐœ์ด ์ง„ํ–‰๋˜๊ฒŒ ๋œ๋‹ค. HTML / JS / CSS๋ฅผ ์–ผ๋งˆ๋‚˜ ์ž˜ ๋‹ค๋ฃจ๋А๋ƒ์— ๋”ฐ๋ผ ์•ฑ ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.
        • ์ฐจ์ด์ ์€ Local HDD๋ฅผ ์ ‘๊ทผ๊ฐ€๋Šฅํ•˜๋А๋ƒ ๋งˆ๋А๋ƒ ์ •๋„๋กœ ์ƒ๊ฐํ•ด๋„ ๋จ
        • ffmpeg ๊ฐ™์ด node.js๋ฅผ ํ†ตํ•ด interact ํ•˜๊ธฐ ์‰ฌ์šด cli process์™€ ์†Œํ†ตํ•˜๊ธฐ๋„ ์ข‹๋‹ค.
      • BrowserWindow ๋ถ€๋ถ„์„ Web App Side๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์€ Keep as generic as posslbie ํ•œ๊ฒŒ ์ข‹๋‹ค. ๊ทธ๋ž˜์•ผ ๋‚˜์ค‘์— ์žฌ์‚ฌ์šฉ์„ฑ์— ์œ ๋ฆฌํ•จ.
    • App <-> BrowserWindow ์†Œํ†ต ๋ฐฉ๋ฒ•?
      • IPC ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•จ
        • IPC: Inter Process Communication
      • const { ipcMain } = require('electron')
        • Main ์—์„œ ์“ฐ์ด๋Š” IPC object.
      • const { ipcRenderer } = require('electron')
        • ๋žœ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” IPC
      • ipcRenderer.send('eventName', 'Data')
        • ํ˜•ํƒœ๋งŒ ๋”ฑ ๋ด๋„ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ์ง€~
        • ์ด๊ฑด ๋žœ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ ๋ฉ”์ธ์œผ๋กœ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด์„œ ์“ฐ๋Š” ๊ฒƒ
        • ๋ฉ”์ธ์—์„œ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ๋Š” ipcMain.on ์„ ์‚ฌ์šฉ
      • mainWindow.webContents.send
        • ๋žœ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋กœ ๋ณด๋‚ผ ๋•Œ ์“ฐ๋Š” ๊ฒƒ. BrowserWindow ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋ณด๋‚ด๊ฒŒ ๋จ
        • ๋ฐ›์„ ๋•Œ๋Š” ipcRenderer.on ์„ ์‚ฌ์šฉ
    • ์˜ˆ์ œ์—์„œ๋Š” ffmpeg ๊ด€๋ จ๋œ ๋™์ž‘์€ BrowserWindow Process ์—์„œ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , App Process์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Œ. ์ด๊ฒŒ ์ข€ ๋” ๋ฐ”๋žŒ์งํ•ด ๋ณด์ด๊ธฐ๋„ ํ•จ.

Status Tray Application

  • Tray ์™€ BrowserWindow ๋Š” ๋‹น์—ฐํ•˜๊ฒŒ๋„ ๋‹ค๋ฅด๋‹ค.
  • Tray Icon์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•?
    • new Tray(iconPath)
    • tray.on('click', () => { mainWindow.show(); }); ์ž์ฃผ ์“ฐ์ด๋Š” ํŒจํ„ด
  • Tray ๊ทผ์ฒ˜์— ์ฐฝ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•? (ํŠนํžˆ OS, ์‚ฌ์šฉ์ž๋งˆ๋‹ค ์œ„์น˜๊ฐ€ ๋‹ค๋ฅผ ๊ฒƒ..)
    • click ์ด๋ฒคํŠธ๊ฐ€ ์˜ฌ ๋•Œ, bounds ๊ฐ์ฒด๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค. ์ด ๊ฐ์ฒด์— x, y ์ขŒํ‘œ๊ฐ€ ์ œ๊ณต๋จ. ์ด๊ฒƒ์„ ์ฐธ๊ณ ํ•˜์—ฌ window ์˜ ์œ„์น˜๋ฅผ ์กฐ์ ˆํ•˜๋ฉด ๋œ๋‹ค.
    • mainWindow์— setBounds๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ง€์ •ํ•œ ์˜์—ญ์— ์œˆ๋„์šฐ๋ฅผ ์ž˜ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

ETC (๋‹ค๋ฅธ ๊ฐ•์˜ ๋‚ด์šฉ ํฌํ•จ)

  • app ์˜ webContent์— new-window ์ด๋ฒคํŠธ์— preventDefault๋ฅผ ๊ฑธ์–ด๋‘๋ฉด ๋งํฌ์—์„œ ์ƒˆ ์ฐฝ์ด ๋œจ๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Œ.
  • session์€ ๋ชจ๋“  browserWindow์™€ ๊ณต์œ  ๋จ. mainWindow.webContents.session
    • const {session} = require('electron') ๋„ ๊ฐ™์€ ๊ฐ’ ์ฐธ์กฐ.
      • session.defaultSession
    • custom session๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ธด ํ•˜๋‹ค.
    • cookie ๋„ session์˜ ํ•˜์œ„ object ์ž„.
    • download ๋„ control ๊ฐ€๋Šฅํ•˜๋‹ค. (์ฐฝ ์•ˆ๋œจ๊ณ  ์ž๋™ ๋‹ค์šด ๊ฐ€๋Šฅ)
    • globalShortcut : ํ™”๋ฉด์ด ๋–  ์žˆ์ง€ ์•Š๋”๋ผ๋„ ์ „์ฒด์ ์œผ๋กœ ๋“ฑ๋ก๋˜๋Š” Shortcut
      • ์‹ฌ์ง€์–ด ์•ฑ์ด ์ผœ์ ธ์žˆ์ง€ ์•Š๋”๋ผ๋„ ์‹คํ–‰๋จ! ๊ทธ๋Ÿฌ๋‚˜ ์ถฉ๋Œ๋‚˜๋Š” ๊ฒƒ์€ ์•Œ์•„์„œ ํšŒํ”ผํ•ด์•ผ ํ•  ๊ฒƒ..
  • const {screen} = require('electron')์„ ํ†ตํ•ด์„œ ์Šคํฌ๋ฆฐ์— ๊ด€๋ จ๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
    • getAllDisplay(): ์ „์ฒด ์—ฐ๊ฒฐ๋œ display์— ๋Œ€ํ•œ ์ •๋ณด๋“ค์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
      • touchSupport ๋“ฑ๋“ฑ ํฌํ•จ..
  • Main Process๊ฐ€ BrowserWindow๋ฅผ ์†Œ์œ ํ•˜๋ฉฐ, Browser Window๋Š” Renderer Process ๊ทธ ์ž์ฒด๋‹ค.
    • Main Process๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ BrowserWindow๋ฅผ ์†Œ์œ ํ•œ๋‹ค.
  • BrowserWindow๋Š” webFrame > webContents ๋ฅผ ์†Œ์œ ํ•œ๋‹ค.
    • ex) Zoom Factor
  • electron.desktopCapturer: ๋ฐ์Šคํฌํƒ‘์˜ ํ™”๋ฉด์„ ์ €์žฅ ๊ฐ€๋Šฅํ•จ
  • process.version: ๊ฐ ๋ฒ„์ „์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ..
    • ex) chrome, napi, electron, v8, opensssl ๋“ฑ๋“ฑ..
  • process.hang(): Renderer๋ฅผ Hang์„ ๊ฑธ ์ˆ˜ ์žˆ์Œ.
  • process.crash(): crash ๋  ๋•Œ๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ดํŒ… ํ•  ์ˆ˜ ์žˆ์Œ.
    • ์ด ๋•Œ reload ๊ฑธ๋ฉด ๋ณต๊ตฌ๋  ์ˆ˜ ์žˆ์Œ. ๋ณต๊ตฌ ๋˜๋Š”๊ฒŒ ๋‚ซ๋‚˜?..
  • ๋ช‡๋ช‡ electron API๋“ค์€ Shared API๋กœ, renderer์™€ main ๋ชจ๋‘์—๊ฒŒ์„œ ์“ฐ์ผ ์ˆ˜ ์žˆ์Œ. (ex: shell ๋“ฑ..)
    • electron.remote ์•ˆ์จ๋„ ๋œ๋‹ค๋Š” ๋œป

Distribution

  • Electron-Builder ๊ฐ€ ์ง„๋ฆฌ.
    • cli ์˜ต์…˜๋„ ์ง€์›ํ•œ๋‹ค. ์–ด๋–ค platform์œผ๋กœ distribute ํ• ์ง€๋Š” ์ด๊ฑธ๋กœ ๊ฒฐ์ •
    • ๊ฒฐ๊ณผ๋ฌผ์€ dist ๋””๋ ‰ํ† ๋ฆฌ์— ์ œ๊ณต๋จ.
  • ์ œ๋Œ€๋กœ ํ•˜๋ ค๋ฉด code-signing๋„ ํ•ด์•ผ ํ•จ. (์•„๋งˆ mac์€ ๋” ๊ฐ•์ œ๋˜๋Š”๋“ฏ?)
  • Electron-Builder์— ์ด๋ฏธ AutoUpdater๊ฐ€ ์žˆ๋‹ค.
    • GitHub ๋ฅผ ํ†ตํ•ด ๋ฐฐํฌํ•˜๋ ค๋ฉด, Personal access token์„ ๋ฐœ๊ธ‰๋ฐ›์•„์•ผ ํ•œ๋‹ค.
    • ์ด๋ฅผ ๋“ฑ๋กํ•˜๋ฉด, ๋นŒ๋“œ ํ›„ ์ž๋™์œผ๋กœ GitHub release์— ์˜ฌ๋ ค๋ฒ„๋ฆด ์ˆ˜ ์žˆ์Œ. (์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ tag๋ฅผ ์ฐธ์กฐํ•ด์„œ ์˜ฌ๋ฆฌ๋Š” ๋“ฏ?)
  • (์ฐธ๊ณ ) setx ์ปค๋งจ๋“œ๋ฅผ ์œˆ๋„์šฐ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ž ์‹œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ž๋™ ์—…๋ฐ์ดํŠธ๋Š” electron-updater์™€ ํ•จ๊ป˜~
๐Ÿท๏ธ ์ฃผ์ œ ๋ชฉ๋ก: