Electron: ๋ฐ˜์‘์—์„œ ์ „์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜ | '์ „์ž'์—์„œ { ipcRenderer } ๊ฐ€์ ธ์˜ค๊ธฐ

์— ๋งŒ๋“  2017๋…„ 07์›” 03์ผ  ยท  65์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: electron/electron

create-react-app์œผ๋กœ ๊ฐ„๋‹จํ•œ ๋ฐ˜์‘ ์•ฑ์„ ๋งŒ๋“ค๊ณ  ์ „์ž์™€ ์„ฑ๊ณต์ ์œผ๋กœ ํ†ตํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์•ก์…˜ ์ƒ์„ฑ์ž ํŒŒ์ผ์— ์ „์ž๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์ „๊นŒ์ง€ ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ค„์„ ์ œ๊ฑฐํ•˜๋ฉด ์•ฑ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ipcRenderer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ˜์‘ ์ธก์—์„œ ์ „์ž ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ์ค„๋กœ ์ธํ•ด ์•ฑ์ด ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค.
import { ipcRenderer } from 'electron';

๋‹ค์Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

TypeError: fs.existsSync๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
(์ต๋ช… ๊ธฐ๋Šฅ)
node_modules/electron/index.js:6

  3 | 
  4 | var pathFile = path.join(__dirname, 'path.txt')
  5 | 
> 6 | if (fs.existsSync(pathFile)) {
  7 |   module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
  8 | } else {
  9 |   throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')

์ „์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•  ๋•Œ ์ด๊ฒƒ์ด ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ๋ผ๋Š” ๊ฒƒ์„ Google์—์„œ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๋„์™€ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@MarshallOfSound ๋‚ด ์‹ค์ˆ˜.

๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ฌธ์ œ #7300์—์„œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

const { ipcRenderer } = window.require('electron');

์ด๊ฒƒ์€ Electron ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ ์ž‘๋™ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ๋‚ด์—์„œ React ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค(window.require๋Š” Electron์—์„œ์™€ ๊ฐ™์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ •์˜๋˜์ง€ ์•Š์Œ).

๋ชจ๋“  65 ๋Œ“๊ธ€

CRA๋Š” ํ‘œ์ค€ ๋ชจ๋“ˆ ๋กœ๋”ฉ(fs ํฌํ•จ)์„ ์—‰๋ง์œผ๋กœ ๋งŒ๋“œ๋Š” ์›นํŒฉ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

webpack์˜ Electron ๋ชจ๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  CRA์—์„œ ๊บผ๋‚ด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

GitHub ๋ฌธ์ œ๋Š” ๊ธฐ๋Šฅ ์š”์ฒญ ๋ฐ ๋ฒ„๊ทธ ๋ณด๊ณ ์šฉ์ด๋ฉฐ Electron ์‚ฌ์šฉ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์€ ์ปค๋ฎค๋‹ˆํ‹ฐ ๋˜๋Š” Slack ์ฑ„๋„๋กœ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@MarshallOfSound ๋‚ด ์‹ค์ˆ˜.

๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ฌธ์ œ #7300์—์„œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

const { ipcRenderer } = window.require('electron');

์ด๊ฒƒ์€ Electron ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ ์ž‘๋™ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ๋‚ด์—์„œ React ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค(window.require๋Š” Electron์—์„œ์™€ ๊ฐ™์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ •์˜๋˜์ง€ ์•Š์Œ).

app.quit()์— ์•ก์„ธ์Šคํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const { ์•ฑ } = window.require('์ „์ž').remote;

๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋ ์ง€๋„...

@CiriousJoker ์ด๊ฒƒ์€ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‚˜๋Š” ์—ฌ์ „ํžˆ window.require is not a function ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” React Starter Kit(https://github.com/kriasoft/react-starter-kit)์™€ ํ•จ๊ป˜ Electron์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์›น์—์„œ ์•ฑ์„ ๋กœ๋“œํ•˜๋„๋ก Electron ์•ฑ์„ ์„ค์ •ํ–ˆ์œผ๋ฏ€๋กœ ์•ฑ์ด ๋กœ์ปฌ์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

๋‚ด๊ฐ€ํ•˜๋ ค๋Š” ๊ฒƒ์€ ๋‚ด React ํŒŒ์ผ ์ค‘ ํ•˜๋‚˜์—์„œ ipcRenderer ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด ์•ฑ์ด ์›น์—์„œ ๋กœ๋“œ๋  ๋•Œ๋„ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ œ์•ˆ?

@holgersindbaek

๋‹น์‹ ๊ณผ ๊ฐ™์€ ๋ฐฐ์—์„œ... ๋‹น์‹ ์€ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๊นŒ?

์•„๋‹ˆ์š”. ๋ธŒ๋ผ์šฐ์ €์—์„œ ipcRenderer๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €์—์„œ React ์•ฑ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Electron ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•˜๋ฉด ๊ดœ์ฐฎ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@Amthieu ์กฐ์–ธ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๋‚ด React ํ”„๋กœ์ ํŠธ(React Starter Kit ๊ธฐ๋ฐ˜)๋ฅผ Electron์—์„œ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ํ™•์‹ ์ด ์„œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์กฐ์–ธ์„ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

https://discuss.atom.io/t/getting-electron-to-work-with-react-starter-kit/48594

๋„ค, ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1) ๋‹ค์Œ ์ฝ”๋“œ๋กœ preload.js file ์ƒ์„ฑ:

window.ipcRenderer = require('electron').ipcRenderer;

2) webPreferences ๋ฅผ ํ†ตํ•ด main.js์— ์ด ํŒŒ์ผ์„ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

3) ์ด์ œ ๋ฐ˜์‘ ์•ฑ์—์„œ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

componentDidMount() {
        if (isElectron()) {
            console.log(window.ipcRenderer);
            window.ipcRenderer.on('pong', (event, arg) => {
                this.setState({ipc: true})
            })
            window.ipcRenderer.send('ping')
        }
    }

์ฐธ๊ณ  - isElectron() ํ•จ์ˆ˜์— ๋Œ€ํ•ด https://github.com/cheton/is-electron ์‚ฌ์šฉ

@HemalR 3๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค(ํ˜„์žฌ):

componentDidMount() {
    if (window.isElectron) {
        console.log(window.ipcRenderer);
        window.ipcRenderer.on('pong', (event, arg) => {
            this.setState({ipc: true})
        })
        window.ipcRenderer.send('ping')
    }
}

์ฐธ๊ณ  : window.isElectron ๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

@nparsons08

์‚ฌ๊ณผ - isElectron์„ ๋ฐ›๋Š” ์œ„์น˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์–ด์•ผ ํ•˜๋ฉฐ, https://github.com/cheton/is-electron ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ์˜ˆ์ œ๋ฅผ ํŽธ์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

@holgersindbaek
์ง€๊ธˆ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๊นŒ

์ €๋ฅผ ์œ„ํ•ด nodeIntegration ์ด true ๊ฒฝ์šฐ์—๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

webPreferences: {
      nodeIntegration: true, 
      preload: __dirname + '/preload.js'
}

@HemalR ์†”๋ฃจ์…˜์„

์ด์ œ ์ „์ž์—์„œ ๋ฐ˜์‘์œผ๋กœ ๋ณด๋‚ด๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํ•จ๊ป˜ ์‹œ๋„
์ „์ž์ชฝ์—

 ipcMain.emit("pong", "Hello!"); 

๊ทธ๋Ÿฌ๋‚˜ React ๋ฆฌ์Šค๋„ˆ๋กœ๋ถ€ํ„ฐ ์•„๋ฌด ๊ฒƒ๋„ ์ˆ˜์‹ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

window.ipcRenderer.on("pong", (event, arg) => {
        console.log("PONG");
});

ipcMain.emit()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋งž์Šต๋‹ˆ๊นŒ ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ๊ฒƒ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

์ข‹์•„, ๋‚˜๋Š” (์ „์ž ์ฃผ ํ”„๋กœ์„ธ์Šค์—์„œ) ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

mainWindow.webContents.send("pong", "Hello!");

๋ชจ๋‘์—๊ฒŒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์œ„์˜ ๋ชจ๋“  ๊ฒƒ์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์•„๋ฌด ์†Œ์šฉ์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•œ ๊ฒƒ์€ ๊ฑฐ๋Œ€ํ•œ ํ•ดํ‚น์ด์—ˆ์Šต๋‹ˆ๋‹ค. ./node_modules/electron/index.js ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๊ณ  ๊ฒฝ๋กœ๋ฅผ electron.exe ํ•˜๋“œ ์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ

function getElectronPath() {
  return 'D:\\Repos\\MyProject\\node_modules\\electron\\dist\\electron.exe';
}

module.exports = getElectronPath();

์™€์šฐ, ๋‚ด React ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ž‘๋™ํ•˜๋Š” IPCRenderer๋ฅผ ์–ป์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์œ„์˜ ๋ชจ๋“  ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜น์‹œ ๋‚ด๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํžŒํŠธ๊ฐ€ ์žˆ์œผ์‹ ๊ฐ€์š”? ๊ฐ์‚ฌ

ํ ... ๋‚ด ์ „์ž ์•ฑ์€ ์œ„์˜ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ์ „ํžˆ ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ ๋ช‡ ๋‹ฌ ๋™์•ˆ ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค(ํ•„์š”ํ•˜์ง€ ์•Š์Œ).

์ด๊ฒƒ์ด ์ž‘๋™ํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ „์ž ๋ฒ„์ „์„ ๊ฒŒ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@cyclonstep ๋ฐœ์ƒํ•œ ํŠน์ • ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์ฝ”๋“œ ์กฐ๊ฐ์ด๋‚˜ ์ผ๋ถ€ ๋กœ๊ทธ ์—†์ด๋Š” ๋•๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค...

๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ  ์†Œํฌ๋ฅผ ๋ฌถ์–ด.
Window.require๋Š” ๋˜ํ•œ ๋‚˜๋ฅผ ์œ„ํ•ด ๊ทธ๊ฒƒ์„ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค (๋˜ํ•œ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์€ ๊ฒƒ์„ ๋ณด์—ฌ์คŒ).

'vue/dist/vue.min'์—์„œ Vue ๊ฐ€์ ธ์˜ค๊ธฐ
'./App'์—์„œ ์•ฑ ๊ฐ€์ ธ์˜ค๊ธฐ

// ๋‚˜์œ? '์ „์ž'์—์„œ { ipcRenderer } ๊ฐ€์ ธ์˜ค๊ธฐ
// ๋‚˜์œ? const { ipcRenderer } = ์š”๊ตฌ('์ „์ž')
// ์ข‹์€:
const { ipcRenderer } = window.require('์ „์ž')

(๋™์ผํ•œ ํŒŒ์ผ์˜ ๋” ์•„๋ž˜์—๋Š” ์ „์ž "pong-Demo"๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค . ์ด๋Š” ์ผ์ข…์˜ ์ฆ๋ช…, ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค)

์•„๋งˆ๋„ ์ฃผ๋ชฉํ•  ๋งŒํ•ฉ๋‹ˆ๋‹ค. ์ž˜๋ชปํ–ˆ์„ ๋•Œ์—๋„ ๋ฒˆ๋“ค ํฌ๊ธฐ๋Š” ์ „์ฒด Electron ํฌ๊ธฐ๋งŒํผ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š์ง€๋งŒ(electron-require๊ฐ€ ์—†๋Š” ๊ฒƒ๊ณผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ง€๊ธˆ๊นŒ์ง€ ์ €์˜ ์ฒซ ๋ฒˆ์งธ์ด์ž ์œ ์ผํ•œ ๋ Œ๋” ์ธก ์ „์ž ๊ฐ€์ ธ์˜ค๊ธฐ์ž…๋‹ˆ๋‹ค) ์•ฝ 20kb ์ •๋„๋งŒ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. , node_modules/electron-download/node_modules/debug/dist/debug.js :242:ff์—์„œ ์˜ค๋Š” ์ž์ฒด์ ์œผ๋กœ ์ผ๋ถ€ shim/wrapper ์ฝ”๋“œ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

2: [function (require, module, exports) {
  // shim for using process in browser
  var process = module.exports = {}; // cached from whatever global is present so that test runners that
 stub it

์–ด์จŒ๋“ , ์ผ์ด ์œ„์—์„œ ๋งํ•œ ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋…ธ๋“œ ๋ฒ„์ „ 10.2.0
ํฌ๋กฌ ๋ฒ„์ „ 66.0.3359.181
์ „์ž ๋ฒ„์ „ 3.0.2

window.require ์˜ค๋ฅ˜ window is not defined ์™€ ํ•จ๊ป˜ ๋‚ด ๊ธฐ๋ณธ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ const electron = eval('require')("electron") ๋กœ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋•๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. webpack์„ ์‚ฌ์šฉํ•˜๊ณ  ๋ฌธ์ œ๋Š” webpack์ด ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๋‚ด require ๋ฌธ์„ ํ‰๊ฐ€ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@MarshallOfSound ๋‚ด ์‹ค์ˆ˜.

๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ฌธ์ œ #7300์—์„œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

const { ipcRenderer } = window.require('electron');

์ด๊ฒƒ์€ Electron ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ ์ž‘๋™ํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ๋‚ด์—์„œ React ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค(window.require๋Š” Electron์—์„œ์™€ ๊ฐ™์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ •์˜๋˜์ง€ ์•Š์Œ).

๊ทธ๋ฆฌ๊ณ  ํƒ€์ดํ”„์Šคํฌ๋ฆฝํŠธ์˜ ๊ฒฝ์šฐ:

import {IpcRenderer} from 'electron';

declare global {
  interface Window {
    require: (module: 'electron') => {
      ipcRenderer: IpcRenderer
    };
  }
}

const { ipcRenderer } = window.require('electron');

@moshfeu ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์€ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋‚ด React ํ”„๋กœ์ ํŠธ์—์„œ IpcRenderer๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Webpack์ด๋‚˜ Browserfy๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œํ•œ๋ฒˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค :D

typescript์˜ ๊ฒฝ์šฐ ์œ„์˜ @HemalR ์˜ˆ์ œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ nodeIntegration: true ์—†์ด : https://github.com/electron/electron/issues/9920#issuecomment -336757899:

๋„ค, ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ preload.js file ์ƒ์„ฑ:
window.ipcRenderer = require('electron').ipcRenderer;
  1. webPreferences ๋ฅผ ํ†ตํ•ด ์ด ํŒŒ์ผ์„ main.js์— ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜์„ธ์š”.
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. ์ด์ œ ๋ฐ˜์‘ ์•ฑ์—์„œ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

์ฐธ๊ณ  - isElectron() ํ•จ์ˆ˜์— ๋Œ€ํ•ด https://github.com/cheton/is-electron ์‚ฌ์šฉ

์™€ ๊ฒฐํ•ฉ
https://github.com/electron/electron/issues/9920#issuecomment -447157348

๋‚˜๋Š” ์ด๊ฒƒ์„ ์‚ฌ์šฉํ–ˆ๋‹ค:

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

๊ทธ๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋•๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค! stenciljs์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋ฉฐ ๋ฐ˜์‘ ๋ฐ ๊ฐ๋„๋ฅผ ์ƒ์ƒํ•ฉ๋‹ˆ๋‹ค.

webpack ์„ค์ •์—์„œ target: "electron-renderer"๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ {
...
๋Œ€์ƒ: "์ „์ž ๋ Œ๋”๋Ÿฌ"
...
}

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” CRA + Electron์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  '์ „์ž ๋ Œ๋”๋Ÿฌ'๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œ ํด๋”์—์„œ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋Š” ๋™์•ˆ ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ url localhost:3000 ๊ฐœ๋ฐœํ•˜๊ณ  ๋กœ๋“œํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฐ˜์‘ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ node api์™€ electron api๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ์—๊ฒŒ๋‚˜ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ฐ์‚ฌ ํ•ด์š”.

TypeError: fs.existsSync is not a function

์˜ˆ, fs API๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ƒํ™ฉ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ–ˆ๋‹ค๋ฉด react-scripts start (CRA์—์„œ npm start ์˜ ๊ธฐ๋ณธ ์Šคํฌ๋ฆฝํŠธ)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€ electron . ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฌธ์„œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: https://electronjs.org/docs/tutorial/first-app.

๊ทธ๊ฒƒ์ด ๋‹น์‹ ์„ ์œ„ํ•ด ์ž‘๋™ํ•œ๋‹ค๋ฉด LMK. ๋‚ด ์•ฑ์—์„œ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. - https://github.com/moshfeu/y2mp3 (CRA๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Œ์„ ์ฐธ๊ณ 

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” CRA + Electron์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  '์ „์ž ๋ Œ๋”๋Ÿฌ'๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œ ํด๋”์— ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋Š” ๋™์•ˆ ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ localhost:3000 url์„ ๊ฐœ๋ฐœํ•˜๊ณ  ๋กœ๋“œํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฐ˜์‘ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ node api์™€ electron api๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ์—๊ฒŒ๋‚˜ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ฐ์‚ฌ ํ•ด์š”.

TypeError: fs.existsSync is not a function

webpack ์„ค์ •์—์„œ target: "electron-renderer"๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ {
...
๋Œ€์ƒ: "์ „์ž ๋ Œ๋”๋Ÿฌ"
...
}

์ €์—๊ฒŒ๋„ ํšจ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ๋„๊ตฌ ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์›นํŒฉ: "3.8.1"
  • ์ „์ž: "4.2.1"

๋‚ด ์ƒ๊ฐ electron-renderer ์ด ๋ฌธ์ œ ์•ผ ๊ฐ™์ด ์œ ์„  ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค ์ „์ž ํ•ด๊ฒฐ ๋‹จ์ง€์— ๋Œ€ํ•ด window.required , ๊ทธ๋ฆฌ๊ณ  ์‹ฌ์ง€์–ด ์†์‹ค ์ž…๋ ฅ์„!

๋‚˜๋ฅผ ์œ„ํ•ด ์„ค์ • ๋Œ€์ƒ: webpack ๋ฐ nodeIntegration์— ๋Œ€ํ•œ "์ „์ž ๋ Œ๋”๋ง": BrowserWindow ์˜ต์…˜์—์„œ true
์˜ค๋Š˜ ์ตœ์‹  ๋ฒ„์ „์˜ webpack+electron+react๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

TypeScript์™€ Electron์„ ์‚ฌ์šฉํ•˜์—ฌ create-react-app์„ ์‹คํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์„ค์ •์„ ์œ„ํ•ด ์ด ์•„์ฃผ ์ข‹์€ ์ง€์นจ ์„ ๋”ฐ๋ž์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๋˜ํ•œ์ด ์˜ค๋ฅ˜์— ๋ถ€๋”ช์ณค๋‹ค. ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•˜๋Š” ์†”๋ฃจ์…˜์€ ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋งํ•œ ๋‚ด์šฉ์˜ ํ•ฉ๊ณ„์ž…๋‹ˆ๋‹ค.

  1. ๋ฐ˜์‘ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ "electron-renderer" ๋ฅผ target ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด rescripts ์™€ ํ•จ๊ป˜ rescript-env ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

package.json

  "rescripts": [
    "env"
  ],

.rescriptsrc.js

module.exports = [require.resolve("./webpack.config.js")];

webpack.config.js :

module.exports = config => {
  config.target = "electron-renderer";
  return config;
};
  1. ๋…ธ๋“œ ํ†ตํ•ฉ ์ถ”๊ฐ€
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
});
  1. src/typings.d.ts :
declare var window: Window;
interface Window {
  require: any;
}

๊ทธ๋ฆฌ๊ณ  ๋งˆ์นจ๋‚ด ๋‹น์‹ ์˜ ์•ฑ์—์„œ

App.tsx

const { remote } = window.require("electron");
console.log(remote.getCurrentWindow());

@LucasBombach
์ด๊ฒƒ๋„ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

declare var window: Window;
interface Window {
  require: NodeRequire
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ํ•„์š”ํ•œ consts๋ฅผ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”!

Vue๊ฐ€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์—๊ฒŒ ๋„์›€์ด ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ Vue์— ์‚ฌ์šฉํ•œ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € ์ฐฝ์„ ์ƒ์„ฑํ•  ๋•Œ ์›น ํ™˜๊ฒฝ ์„ค์ •์— ์ถ”๊ฐ€ํ•˜์—ฌ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•ด ๋…ธ๋“œ ํ†ตํ•ฉ์ด ํ™œ์„ฑํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

new BrowserWindow({
    webPreferences: {
        nodeIntegration: true,
    },
})
````

Configure webpack to package your application for electron renderer by adding a target to your `vue.config.js` (or wherever you set your vue settings). 
```js
module.exports = {
    configureWebpack: {
        target: 'electron-renderer',
    },
}

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํ•„์š”ํ•œ ๊ฒƒ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์…ธ ๋ชจ๋“ˆ์„ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

import { shell } from 'electron'

์—ฌ์ „ํžˆ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ. ์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€ ์ฐพ์€ ์ตœ๊ณ ์˜ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค

```js
์ƒˆ ๋ธŒ๋ผ์šฐ์ € ์ฐฝ({
์›น ๊ธฐ๋ณธ ์„ค์ •: {
nodeIntegration: ์ฐธ
}
});

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๊ท€ํ•˜์˜ ์•ฑ์—์„œ fs ๋˜๋Š” ipcRenderer ๊ฐ€์ ธ์˜ค๊ธฐ์— ๋Œ€ํ•ด ๋ฌป๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋Œ“๊ธ€์ด ์ฃผ๋ชฉ๋ฐ›๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ „์ž ์•ฑ์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ์š”๊ตฌ ์‚ฌํ•ญ์ด์ง€๋งŒ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๊ตฌ์‹ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. tl;dr - ๋…ธ๋“œ ๋ชจ๋“ˆ(์˜ˆ: fs ) ๋˜๋Š” ์ „์ž ๋ชจ๋“ˆ(์˜ˆ: ipcRenderer )์„ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์œผ๋ฉด ๋ณด์•ˆ ์ทจ์•ฝ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์•ฑ์„ ์ž์‹ ๋งŒ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ _์•„๋งˆ๋„_ ์•ˆ์ „ํ•˜์ง€๋งŒ ์•ฑ์„ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ํŒ๋งคํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ๋ฏธ๋ฆฌ ์ฝ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ

์†”๋ฃจ์…˜์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— ๋จผ์ € ์šฐ๋ฆฌ๊ฐ€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” _์ด์œ _๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. Electron ์•ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ฑ์— ๋…ธ๋“œ ๋ชจ๋“ˆ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋†€๋ผ์šด ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์•ฑ์ด ๊ธฐ๋ณธ OS(์ฆ‰, ๋…ธ๋“œ) ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉํ•˜๊ณ  ์‹ถ์ง€๋งŒ ๋‚จ์šฉ ๋˜๋Š”

@raddevus ๊ฐ€ ์ฃผ์„์—์„œ ์–ธ๊ธ‰ nodeIntegration:true ์ผœ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์—ฌ์ „ํžˆ nodeIntegration:false ๋ฅผ ์œ ์ง€ํ•˜์—ฌ ์•ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ์šฐ๋ฐœ์ /์•…์˜์ ์ธ ์‚ฌ์šฉ์ž๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์ปดํ“จํ„ฐ์— ์„ค์น˜๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅํ•œ ๋งฌ์›จ์–ด๊ฐ€ ์ „์ž ์•ฑ๊ณผ ์ƒํ˜ธ ์ž‘์šฉํ•˜๊ณ  nodeIntegration:true ๊ณต๊ฒฉ ๋ฒกํ„ฐ(๋งค์šฐ ๋“œ๋ฌผ์ง€๋งŒ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ)!

์‰ฌ์šด ๋ฐฉ๋ฒ•

BrowserWindow์—์„œ nodeIntegration: true ๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋…ธ๋“œ ๋ชจ๋“ˆ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. _์ด๊ฒƒ_์„ ํ•˜๋Š” ๊ฒƒ์€ ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. require("fs") ๋ฐ require("electron") ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋Š” ๋ˆ„๊ตฐ๊ฐ€ XSS ์ทจ์•ฝ์ ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ ๋…ธ์ถœ๋œ ๋ชจ๋“  ๋ช…๋ น ์„

์ปดํ“จํ„ฐ์— ์žˆ๋Š” ๋ชจ๋“  ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ •๋ง ๋‚˜์œ ๊ฒƒ์„ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์„ ์ƒ๊ฐํ•ด ๋ณด์‹ญ์‹œ์˜ค.

(๋Œ€์ฒด) ์‰ฌ์šด ๋ฐฉ๋ฒ•

nodeIntegration์„ true๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ํ•จ๊ป˜ ์•ฑ์ด ์›นํŒฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒŒ์ผ์„ ๋ฌถ์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. Webpack์€ ํŠน์ • ๊ธฐํ˜ธ๋ฅผ ์—‰๋ง์œผ๋กœ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— target: 'electron-renderer' ๋˜๋Š” webpack ์™ธ๋ถ€์™€ ๊ฐ™์€ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๋ณ€์ˆ˜( ipcRenderer )๋ฅผ ๋Œ€์‹  ์•ฑ์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜๋„ ์•ฑ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ• ์™ธ์—๋Š” ์•„๋ฌด ๊ฒƒ๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

(๋‹ค๋ฅธ ๋Œ€์•ˆ) ์‰ฌ์šด ๋ฐฉ๋ฒ•

ipcRenderer ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ์›๊ฒฉ ๋ชจ๋“ˆ ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ '์‰ฌ์šด ๋ฐฉ๋ฒ•'์ž…๋‹ˆ๋‹ค. Electron์˜ ๋ณด์•ˆ ๊ถŒ์žฅ ์‚ฌํ•ญ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ๊ณต๊ฒฉ์ด ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๋ฒกํ„ฐ์— ์˜ํ–ฅ์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค .

์ฆ‰. ์›๊ฒฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ js-object์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•˜๊ณ  ์ปดํ“จํ„ฐ/์•ฑ์—์„œ ํฐ ํ˜ผ๋ž€์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

_๊ฑฐ์˜_ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•

@marksyzm ์€ ์™„๋ฒฝํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋” ๋‚˜์€ ์†”๋ฃจ์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ IPC๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ipcRenderer ๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์— ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์„ค์ •์€ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๊ณต๊ฒฉ์—๋„ ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์•ฑ์„ 80% ์ •๋„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ† ๋ง์„ ๋งŽ์ด ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์˜ณ์€ ๊ธธ

fs / ipcRenderer ๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋กœ ๊ฐ€์ ธ์˜ค๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€ IPC(ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ )๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค์™€ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์‚ฌ์ด์— ๋Œ€ํ™”๋ฅผ ํ—ˆ์šฉํ•˜๋Š” Electron์˜ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋ถ„ํ•ดํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์•ฑ์ด ํ‘œ์‹œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  1. BrowserWindow์—๋Š” preload ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์†์„ฑ์€ require ๋Œ€ํ•œ ์•ก์„ธ์Šค ๊ถŒํ•œ์œผ๋กœ ๋กœ๋“œ๋˜๋Š” js ํŒŒ์ผ์ž…๋‹ˆ๋‹ค(์ฆ‰, ipcRenderer๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ).
  2. BrowserWindow์—๋Š” ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ contextIsolation: true ์žˆ์ง€๋งŒ ์ด๋Š” ipcRenderer๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์— ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด contextBridge ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  3. ์‚ฌ์ „ ๋กœ๋“œ ์Šคํฌ๋ฆฝํŠธ์™€ contextBridge๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ipcRenderer ์— ์•ก์„ธ์Šคํ•˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  4. ๊ธฐ๋ณธ ์Šคํฌ๋ฆฝํŠธ์—์„œ ipcRenderer( ipcMain ๋ชจ๋“ˆ์—์„œ)์— ๋Œ€ํ•œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฆฌ์Šค๋„ˆ ๋‚ด์—์„œ fs ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

_๋Œ€๋žต_ ์ด ๋ชจ๋“  ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฉ”์ธ.js

const {
  app,
  BrowserWindow,
  ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false, // is default value after Electron v5
      contextIsolation: true, // protect against prototype pollution
      enableRemoteModule: false, // turn off remote
      preload: path.join(__dirname, "preload.js") // use a preload script
    }
  });

  // Load app
  win.loadFile(path.join(__dirname, "dist/index.html"));

  // rest of code..
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
  fs.readFile("path/to/file", (error, data) => {
    // Do something with file contents

    // Send result back to renderer process
    win.webContents.send("fromMain", responseObj);
  });
});

preload.js

const {
    contextBridge,
    ipcRenderer
} = require("electron");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

index.html

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>Title</title>
</head>
<body>
    <script>
        window.api.receive("fromMain", (data) => {
            console.log(`Received ${data} from main process`);
        });
        window.api.send("toMain", "some data");
    </script>
</body>
</html>

์ตœ์†Œํ•œ ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ์œ„ํ•ด์„œ๋Š” ์ „์ž v7์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์ด๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ €๋Š” ๋ณด์•ˆ ์ „์ž ์•ฑ์— ๊ด€์‹ฌ์ด ์žˆ์œผ๋ฉฐ ๋ณด์•ˆ์„ ์‚ฌํ›„ ์ƒ๊ฐ์œผ๋กœ ์ƒ๊ฐํ•˜๋Š” ๋Œ€์‹  ๋ณด์•ˆ์„ ๋ฒ ์ดํฌ ์ธ ์ „์ž ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด secure-electron-template ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ @reZach ์ฃผ์„์„ preload.js ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”์ธ.js

let newWindow = null;
function createWindow() {
    newWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "preload.js")
        }
    });

    newWindow.webContents.on('did-finish-load', () => {
        newWindow.webContents.send('APP_MY_INIT', { data: 'hi' });
    });
}

ipcMain.on('APP_SOMETHING', (event, ...args) => {
    // ...
});

ipcMain.handle('APP_SOMETHING_ELSE', (event, ...args) => {
    // ...
    return myVar;
});

preload.js

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

function callIpcRenderer(method, channel, ...args) {
    if (typeof channel !== 'string' || !channel.startsWith('APP_')) {
        throw 'Error: IPC channel name not allowed';
    }
    if (['invoke', 'send'].includes(method)) {
        return ipcRenderer[method](channel, ...args);
    }
    if ('on' === method) {
        const listener = args[0];
        if (!listener) throw 'Listener must be provided';

        // Wrap the given listener in a new function to avoid exposing
        // the `event` arg to our renderer.
        const wrappedListener = (_event, ...a) => listener(...a);
        ipcRenderer.on(channel, wrappedListener);

        // The returned function must not return anything (and NOT
        // return the value from `removeListener()`) to avoid exposing ipcRenderer.
        return () => { ipcRenderer.removeListener(channel, wrappedListener); };
    }
}

contextBridge.exposeInMainWorld(
    'myIpcRenderer', {
        invoke: (...args) => callIpcRenderer('invoke', ...args),
        send: (...args) => callIpcRenderer('send', ...args),
        on: (...args) => callIpcRenderer('on', ...args),
    },
);

ํด๋ผ์ด์–ธํŠธ.js

const { myIpcRenderer } = window;

const removeMyListener = myIpcRenderer.on('APP_MY_INIT', data => {
    console.log(data);
    myIpcRenderer.send('APP_SOMETHING', 'foo');
})

async function test() {
    const result = await myIpcRenderer.invoke('APP_SOMETHING_ELSE', 'foo', 'bar');
    console.log(result);
}
test();

if (/* should remove listener === true */) {
    removeMyListener();
}

๊ทธ๋ฆฌ๊ณ  TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด types.d.ts

declare global {
    interface Window {
        myIpcRenderer: MyIpcRenderer,
    }
}

export interface MyIpcRenderer {
    invoke(channel: string, ...args: any[]): Promise<any>;
    send(channel: string, ...args: any[]): void;

    /** <strong i="22">@return</strong> A function that removes this listener. */
    on(channel: string, listener: (...args: any[]) => void): () => void;
}

์ด๊ฒƒ์„ preload.js์— ๋„ฃ์œผ๋ ค๊ณ  ํ•  ๋•Œ ๋ชจ๋“ˆ์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋ฅผ ์•„๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

const errorLogging = require('../renderer/utils/errorLogging');

๊ฐ์‚ฌ ํ•ด์š”

@silentlight ์ด๊ฒƒ์€ ์ „์ž์™€ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€ํ™”๋ฅผ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๋Š” ์ž์‹ ๋งŒ์˜ github ์ €์žฅ์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ( require() ์˜ ๊ฒฝ๋กœ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋” ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๋ณด์ง€ ์•Š๊ณ  ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.)

์ด๋ฏธ window.require ์†”๋ฃจ์…˜์ด ์žˆ์ง€๋งŒ ์žฌ๋ฏธ๋ฅผ ์œ„ํ•ด ์ œ ์†”๋ฃจ์…˜์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

๋ฒˆ๋“ค๋Ÿฌ๋Š” ES ๋ชจ๋“ˆ์ด๋‚˜ CommonJS ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์‹๋ณ„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฒˆ๋“ค์„ ์‹œ๋„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์™œ ์•„์ง ์•„๋ฌด๋„ ์ด๊ฒƒ์„ ์‹œ๋„ํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜†

@marc2332 ์‚ฌ์‹ค์ด์ง€๋งŒ ์ด ๋งˆ๋ฒ• ๋ž˜ํผ์˜ ์ž ์žฌ์ ์ธ ๊ณต๊ฒฉ์— ์ง์ ‘ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ํ™•์‹คํžˆ ๊ณ ๋ คํ•ด์•ผ ํ•  ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

@reZach ์†”์งํžˆ window.require๊ฐ€ ๋” ์•ˆ์ „ํ•œ ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. Btw, ๊ทธ๊ฒƒ์„ํ•˜๋Š” ๋” ๋”๋Ÿฌ์šด ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

const { ipcRenderer } = eval("require('electron')")

@marc2332 ๋‘˜ ๋‹ค ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์›๊ฒฉ ์ž์‚ฐ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฃผ์ž… ๊ณต๊ฒฉ์— ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜„

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค reZach! ์‚ฌ๋žŒ๋“ค์ด ๋‹น์‹ ์˜ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๋” ๋นจ๋ฆฌ ์ดํ•ดํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋˜๋Š” ๋‚˜์ค‘์—

๋งˆํฌ ์—˜ํ•€์Šคํ†ค
www.oxfordsourceltd.com

Zach๋‹˜, contextBridge ์†”๋ฃจ์…˜์ด ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๊นŒ?

๋‚˜์˜ ์›๋ž˜์˜ ์ฝ”๋“œ contextIsolation: false ๋‹จ์ˆœํžˆ์— ๊ธฐ๋Šฅ ๋ถ€์ฐฉ window . ์ด ํ•จ์ˆ˜๋Š” Promise<MediaStream> ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ contextBridge ๋ฌธ์„œ์—๋Š” ํ”„๋กœํ† ํƒ€์ž…์ด ์‚ญ์ œ๋˜๊ณ  contextBridge ์‚ฌ์šฉํ•  ๋•Œ ๋นˆ ๊ฐ์ฒด๊ฐ€ ์ˆ˜์‹ ๋œ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•˜๋ฉด์„œ๋„ contextIsolation: true ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

Zach๋‹˜, contextBridge ์†”๋ฃจ์…˜์ด ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๊นŒ?

๋‚˜์˜ ์›๋ž˜์˜ ์ฝ”๋“œ contextIsolation: false ๋‹จ์ˆœํžˆ์— ๊ธฐ๋Šฅ ๋ถ€์ฐฉ window . ์ด ํ•จ์ˆ˜๋Š” Promise<MediaStream> ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ contextBridge ๋ฌธ์„œ์—๋Š” ํ”„๋กœํ† ํƒ€์ž…์ด ์‚ญ์ œ๋˜๊ณ  contextBridge ์‚ฌ์šฉํ•  ๋•Œ ๋นˆ ๊ฐ์ฒด๊ฐ€ ์ˆ˜์‹ ๋œ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•˜๋ฉด์„œ๋„ contextIsolation: true ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•„๋‹ˆ์˜ค, ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŒ€์› ์ค‘ ํ•œ ๋ช…์ด ์ด๊ฒƒ์ด ์˜๋„๋œ ํ–‰๋™์ด๋ผ๋Š” ์˜๊ฒฌ์„ ์ฐพ์œผ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์„ ์€ Electron v8( 20214 ๋…„

์ฐธ๊ณ : ํ•จ์ˆ˜, DOM ๊ฐœ์ฒด, process.env ๋˜๋Š” WebContents์™€ ๊ฐ™์€ ํŠน์ˆ˜ Node/Electron ๊ฐœ์ฒด ๋˜๋Š” ์ด๋Ÿฌํ•œ ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ๊ฐœ์ฒด์™€ ๊ฐ™์ด V8์˜ ๊ตฌ์กฐ์  ๋ณต์ œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์—†๋Š” ๊ฐœ์ฒด๋Š” ์ด์ „ base::Value-๋กœ ์ง๋ ฌํ™”๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ฐ˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋™์ž‘์€ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ Electron 9๋ถ€ํ„ฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‹น์‹ ์ด ๋“ฃ๊ณ  ์‹ถ์—ˆ๋˜ ์†Œ์‹์ด ์•„๋‹ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

Zach๋‹˜, contextBridge ์†”๋ฃจ์…˜์ด ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๊นŒ?
๋‚˜์˜ ์›๋ž˜์˜ ์ฝ”๋“œ contextIsolation: false ๋‹จ์ˆœํžˆ์— ๊ธฐ๋Šฅ ๋ถ€์ฐฉ window . ์ด ํ•จ์ˆ˜๋Š” Promise<MediaStream> ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ contextBridge ๋ฌธ์„œ์—๋Š” ํ”„๋กœํ† ํƒ€์ž…์ด ์‚ญ์ œ๋˜๊ณ  contextBridge ์‚ฌ์šฉํ•  ๋•Œ ๋นˆ ๊ฐ์ฒด๊ฐ€ ์ˆ˜์‹ ๋œ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ”„๋กœํ† ํƒ€์ž…์„ ์ง€์›ํ•˜๋ฉด์„œ๋„ contextIsolation: true ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•„๋‹ˆ์˜ค, ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŒ€์› ์ค‘ ํ•œ ๋ช…์ด ์ด๊ฒƒ์ด ์˜๋„๋œ ํ–‰๋™์ด๋ผ๋Š” ์˜๊ฒฌ์„ ์ฐพ์œผ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์„ ์€ Electron v8( 20214 ๋…„

์ฐธ๊ณ : ํ•จ์ˆ˜, DOM ๊ฐœ์ฒด, process.env ๋˜๋Š” WebContents์™€ ๊ฐ™์€ ํŠน์ˆ˜ Node/Electron ๊ฐœ์ฒด ๋˜๋Š” ์ด๋Ÿฌํ•œ ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ๊ฐœ์ฒด์™€ ๊ฐ™์ด V8์˜ ๊ตฌ์กฐ์  ๋ณต์ œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์—†๋Š” ๊ฐœ์ฒด๋Š” ์ด์ „ base::Value-๋กœ ์ง๋ ฌํ™”๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ฐ˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋™์ž‘์€ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ Electron 9๋ถ€ํ„ฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‹น์‹ ์ด ๋“ฃ๊ณ  ์‹ถ์—ˆ๋˜ ์†Œ์‹์ด ์•„๋‹ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์•ˆํƒ€๊น์ง€๋งŒ ๋น ๋ฅธ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์œ„์—์„œ ์„ค๋ช…ํ•œ ๋‹จ๊ณ„( @reZach ๋ฐ @aplum ์ œ๊ณต)๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์œผ๋ฉฐ client.js ์—์„œ preload.js ์—์„œ main.js ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒƒ์„ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๋ฉด ํ†ต์‹ ์ด ์ž‘๋™ํ•˜์ง€๋งŒ electron-builder ์‹คํ–‰ํ•˜๋ฉด ํ†ต์‹ ์ด ๋Š์–ด์ง‘๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด setBadge() ํ•˜๋ฉด ๋กœ์ปฌ์—์„œ ์‹คํ–‰๋  ๋•Œ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. client.js ํŒŒ์ผ์—์„œ ์นด์šดํŠธ๋ฅผ ๊ฐ€์ ธ์™€ contextBridge ๋กœ ์ „๋‹ฌ๋œ ๋‹ค์Œ main.js ๋˜๊ณ  app.dock.setBadge(count) ์„ฑ๊ณต์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. electron-builder ๋กœ ๋นŒ๋“œํ•œ ํ›„ Electron์˜ ํ†ต์‹ ์—์„œ ๋ˆ„๋ฝ๋œ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

"electron": "^8.2.3",
"electron-builder": "^22.4.0",

๋ฉ”์ธ.js

const { ipcMain } = electron;

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        height: height,
        width: width,
        minHeight: 575,
        minWidth: 900,
        center: true,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            spellcheck: true,
            preload: path.join(__dirname, "preload.js")
        },
    });
}

ipcMain.on('SEND_BADGE_COUNT', (e, count) => {
    const badgeCount = count > 0 ? count : '';
    app.dock.setBadge(`${badgeCount}`)
})

ํ”„๋ฆฌ๋กœ๋“œ.js

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

contextBridge.exposeInMainWorld(
    'setBadgeCountForElectron', {
        send: (channel, data) => {
            console.log(`Preload ${channel}, ${data}`)
            ipcRenderer.send(channel, data)
        }
    }
)

ํด๋ผ์ด์–ธํŠธ.js

const { setBadgeCountForElectron } = window;

function sendBadgeCount(count) {
  !!setBadgeCountForElectron && setBadgeCountForElectron.send('SEND_BADGE_COUNT', count)
}

sendBadgeCount(count)

ํŒจํ‚ค์ง€.json

  "name": "desktop_app",
  "version": "0.1.8-beta",
  "private": true,
  "description": "",
  "homepage": "./",
  "main": "public/electron.js",
  "build": {
    "copyright": "Copyright ยฉ 2020 My Company",
    "appId": "com.my-app.app",
    "productName": "My Company",
    "buildVersion": "0.0.1-beta",
    "mac": {
      "category": "public.app-category.utilities",
      "icon": "./public/images/mac-icon.png"
    },
    "win": {
      "icon": "./public/images/windows-icon.png"
    },
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },

@SterlingChin , ์ €๋Š” ์ „์ž ํŒ€์˜ ๊ด€๋ฆฌ์ž๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ ์ด๊ฒƒ์€ ์ „์ž ๋นŒ๋” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋” ์ ํ•ฉํ•œ ์งˆ๋ฌธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ž ์‹œ ์ƒ๊ฐํ•ด ๋ณผ ๋•Œ ์ „์ž ๋นŒ๋”๋ณด๋‹ค ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ž ์‹œ ์ƒ๊ฐํ•ด ๋ณผ ๋•Œ ์ „์ž ๋นŒ๋”๋ณด๋‹ค ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋„๋ก ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ๋ถ€๋ถ„์ด ๋ˆ„๋ฝ๋˜์—ˆ๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์‘๋‹ต์„ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. electron-builder ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฌธ์ œ๋ฅผ ์—ด์—ˆ์ง€๋งŒ ๊ด€์‹ฌ์„ ๋ฐ›์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ž ์‹œ ์ƒ๊ฐํ•ด ๋ณผ ๋•Œ ์ „์ž ๋นŒ๋”๋ณด๋‹ค ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋„๋ก ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ๋ถ€๋ถ„์ด ๋ˆ„๋ฝ๋˜์—ˆ๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์‘๋‹ต์„ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. electron-builder ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฌธ์ œ๋ฅผ ์—ด์—ˆ์ง€๋งŒ ๊ด€์‹ฌ์„ ๋ฐ›์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋กœ MVP ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์€ ๋•Œ๋•Œ๋กœ ๋„์›€์ด ๋˜๊ณ  ๊ทธ ๊ณผ์ •์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ๊นจ๋‹ซ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

@Amthieu ์™€ @CiriousJoker ๋‘˜ ๋‹ค ์‚ฌ๋ž‘ํ•ฉ๋‹ˆ๋‹ค ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

  1. ์›์‚ฌ ์ถ”๊ฐ€ -D ์‚ฌ์šฉ์ž ์ •์˜-ํฌ๋ผ ๋ฐ˜์‘-์•ฑ ์žฌ์—ฐ๊ฒฐ
  2. react-app-rewired ์‹œ์ž‘ ์•ฑ์„ ์‚ฌ์šฉํ•˜๋„๋ก package.json ์ˆ˜์ •: "start": "BROWSER=none react-app-rewired start",
  3. ๋ฐ˜์‘ ์•ฑ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— 'config-overrides' ํŒŒ์ผ ์ƒ์„ฑ:
    `const { ์žฌ์ •์˜ } = require('customize-cra');

๊ธฐ๋Šฅ addRendererTarget(๊ตฌ์„ฑ) {
config.target = '์ „์ž ๋ Œ๋”๋Ÿฌ';

๋ฐ˜ํ™˜ ๊ตฌ์„ฑ;
}

module.exports = ์žฌ์ •์˜(addRendererTarget);`

  1. ์ด์ œ '์ „์ž'์—์„œ { ipcRenderer}๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค :)

vuejs๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  vue.config.js์— ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

module.exports = {
"transpileDependencies": [
"๋ทฐํ‹ฐํŒŒ์ด"
],
ํ”Œ๋Ÿฌ๊ทธ์ธ ์˜ต์…˜: {
์ „์ž ๋นŒ๋”: {
nodeIntegration: ์ฐธ
}
}
}

@genilsonmm ํšจ๊ณผ๊ฐ€
๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•œ ํŠน์ • ๋ถ€๋ถ„์€
```์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ
ํ”Œ๋Ÿฌ๊ทธ์ธ ์˜ต์…˜: {
์ „์ž ๋นŒ๋”: {
nodeIntegration: ์ฐธ
}
},

"transpileDependencies": [
"๋ทฐํ‹ฐํŒŒ์ด"
],

๊ทธ๊ฒƒ์€ ๋‚˜์—๊ฒŒ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ... ๊ทธ๋Ÿฌ๋‚˜ window.require('electron')๋Š” ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค

"window.require๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค"๋ผ๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด

์ „์ž์— preoload ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ „์ž ๋ฉ”์ธ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ์— preload.js๋ผ๋Š” ์ด๋ฆ„์˜ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์ด ์ฝ”๋“œ๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.

    window.require = ํ•„์š”;

  2. ์ „์ž ๋ฉ”์ธ ์Šคํฌ๋ฆฝํŠธ๋กœ ์ด๋™ํ•˜์—ฌ ์ฐฝ์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ์— ๋‹ค์Œ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

    ์Šน๋ฆฌ = ์ƒˆ๋กœ์šด BrowserWindow({
    ๋„ˆ๋น„: 1280,
    ํ‚ค: 720,
    ์›น ๊ธฐ๋ณธ ์„ค์ •: {
    ๋…ธ๋“œ ํ†ตํ•ฉ: ๊ฑฐ์ง“,
    ์‚ฌ์ „ ๋กœ๋“œ: __dirname + '/preload.js'
    },

    })
    ์ด๊ฒƒ์œผ๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์—๊ฒŒ๋„ ๋ฐ”๋ž๋‹ˆ๋‹ค :)

@reZach ๊ท€ํ•˜์˜ ์†”๋ฃจ์…˜์ด ์ €์—๊ฒŒ ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์‹ค์ˆ˜ํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ˆ˜๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => fn(...args));
            }
        }

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ "func" ๋กœ ์ •์˜ํ•œ ๋‹ค์Œ "fn"์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์„ค๋ช…ํ•˜๋Š” ๋Œ€๋กœ ์ •ํ™•ํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
ํฐ ์ž์„ธํ•œ ๊ฒŒ์‹œ๋ฌผ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :+1:

@genilsonmm vue.config.js๋ฅผ ๋„ฃ์œผ๋ฉด :

ํ”Œ๋Ÿฌ๊ทธ์ธ ์˜ต์…˜: {
์ „์ž ๋นŒ๋”: {
nodeIntegration: ์ฐธ,

"require is not defined" ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

image

"nodeIntegration: true" ์ค„์„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๋ฉด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ์•ฑ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋ฌด์Šจ ๋œป์ธ๊ฐ€์š”?

@marcoippolito webpack ๋ฒˆ๋“ค ์ฝ”๋“œ๊ฐ€ node js์˜ ๋ชจ๋“ˆ ํ™•์ธ ๋ฐฉ๋ฒ•์ธ require ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋Š” ๋œป์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋…ธ๋“œ ํ†ตํ•ฉ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์ดํ•ด์•ผ ํ•  ๊ฒƒ์€ ๋Œ€์ƒ ๋ธŒ๋ผ์šฐ์ €๋กœ ์›นํŒฉ ์„ค์ •์„ (์ˆ˜์ •์ž…๋‹ˆ๋‹ค var ๋‚ด ๊ธฐ์–ต์ด ๋งž๋‹ค ๊ฒฝ์šฐ ๋Œ€์ƒ) ๋ฐ ๋…ธ๋“œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ์ค‘ ํ•˜๋‚˜์— ํ•ด๋‹นํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์˜ค๋ฅ˜ ๋ฉ€๋ฆฌ ๊ฐ€์•ผํ•œ๋‹ค.

TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ๊ณต์‹ Next.js ์˜ˆ์ œ์— ์ข‹์€ ์˜ˆ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
https://github.com/vercel/next.js/search?q=ipcRenderer&unscoped_q=ipcRenderer

ํ”„๋ฆฌ๋กœ๋“œ.ts

/* eslint-disable @typescript-eslint/no-namespace */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ipcRenderer, IpcRenderer } from 'electron'

declare global {
  namespace NodeJS {
    interface Global {
      ipcRenderer: IpcRenderer
    }
  }
}

// Since we disabled nodeIntegration we can reintroduce
// needed node functionality here
process.once('loaded', () => {
  global.ipcRenderer = ipcRenderer
})

index.tsx

import { useState, useEffect } from 'react'

const Home = () => {
  const [input, setInput] = useState('')
  const [message, setMessage] = useState(null)

  useEffect(() => {
    const handleMessage = (event, message) => setMessage(message)
    global.ipcRenderer.on('message', handleMessage)

    return () => {
      global.ipcRenderer.removeListener('message', handleMessage)
    }
  }, [])

  const handleSubmit = (event) => {
    event.preventDefault()
    global.ipcRenderer.send('message', input)
    setMessage(null)
  }

  return (
    <div>
      <h1>Hello Electron!</h1>
    </div>
  )
}

export default Home

@genilsonmm ํšจ๊ณผ๊ฐ€
๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•œ ํŠน์ • ๋ถ€๋ถ„์€

   pluginOptions: {
        electronBuilder: {
            nodeIntegration: true
        }
    },

ๆญฃ่งฃ, ๆ ‡่ฎฐ

NodeIntegration์ด ํ™œ์„ฑํ™”๋œ ์ƒํƒœ์—์„œ๋„ ์ด ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. window.require ๋ฐ require ๋ชจ๋‘ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

+ ์ด๊ฒƒ์€ ์ผ๋ฐ˜ ์ „์ž๊ฐ€ ์•„๋‹Œ ๋ฐ˜์‘์—์„œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰