📻

Electron에서 명령어 실행하기

Tags
Electron
React
ID matched
Created
Dec 23, 2022 05:01 AM
Last Updated
Last updated July 15, 2023
 
💡
electron-react-boilerplate 템플릿 내에서 명령어를 실행하는 방법에 대해서 서술하였다. 기존 템플릿에서는 뷰 코드에서 node.js의 시스템 명령어를 즉시 사용할 수 있었으나, 최근에 IPC를 통해서 시스템 명령어를 실행할 수 있게 변경해놓아 정리한다.
 
 
 

1. Electron(React, Typescript) 프로젝트 생성

git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git {PROJECT_NAME} cd {PROJECT_NAME} npm install npm start
 
 

2. nodeIntegration 활성화한다.

  • BrowserWindow의 속성 중 webPreferences > nodeIntegration을 true로 설정해준다.
    • mainWindow = new BrowserWindow({ show: false, width: 1024, height: 728, icon: getAssetPath('icon.png'), webPreferences: { nodeIntegration: true, preload: app.isPackaged ? path.join(__dirname, 'preload.js') : path.join(__dirname, '../../.erb/dll/preload.js'), }, });
 
 

3. preload.d.ts에 메소드 타입을 정의한다.

  • 파일은 src/renderer/preload.d.ts에 있다.
    • import { Channels } from 'main/preload'; declare global { interface Window { electron: { ipcRenderer: { sendMessage(channel: Channels, args: unknown[]): void; on( channel: Channels, func: (...args: unknown[]) => void ): (() => void) | undefined; once(channel: Channels, func: (...args: unknown[]) => void): void; output(args: any[], func: (data: string) => void): void; input(args: any[], func: (data: string) => void): void; state(args: any[], func: (data: string) => void): void; }; }; } } export {};
 
 

4. preload.ts에 메소드 내용을 구현한다.

  • 파일은 src/main/preload.ts에 있다.
  • assets 경로 하위에 scripts/state.py 파일을 실행하는 내용으로 구현하였다.
    • assets 내부의 스크립트를 실행하면 경로를 유의해야 한다. (getScriptPath 함수 참고)
      • 디버깅 상태에서는 assets 경로 하위로 설정하면 된다.
      • 빌드 상태에서는 process.resourcesPath 경로 하위로 설정하면 된다.
      import { exec, spawn } from 'child_process'; import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; import path from 'path'; import process, { electron } from 'process'; import { dlog, isDebug } from 'utils/dev'; export type Channels = 'ipc-example'; const getScriptPath = (file_name: string) => { if (isDebug()) return `assets/scripts/${file_name}`; else { return path.join(process.resourcesPath, `assets/scripts/${file_name}`); } }; contextBridge.exposeInMainWorld('electron', { ipcRenderer: { ... state(args: any[], func: (data: string) => void) { const scriptPath = getScriptPath('state.py'); dlog(args[0], args[1]); exec( `python3 ${scriptPath} ${args[0]} ${args[1]}`, (err: any, stdout: any, stderr: any) => { dlog(err, stdout, stderr); func(stdout); } ); }, }, });
 
 

5. 렌더링 부분에서 명령을 호출한다.

  • window.electron.ipcRenderer로 해당 함수를 불러올 수 있다.
    • window.electron.ipcRenderer.state( [pin['fan'], pin['pump']], (data: string) => { const json = JSON.parse(data); dlog(json, json['data']); const success = json['result'] == true; if ((success && !isDebug()) || (!success && isDebug())) { setOutput({ fan: { ...output['fan'], value: json['data'][0] == 1, }, pump: { ...output['pump'], value: json['data'][1] == 1, }, }); } } ); }, []);