Electron ์‹œ์ž‘ํ•˜๊ธฐ (2) ๐Ÿƒโ€โ™€๏ธ

๋ฐ•ํฌ์ˆ˜ยท2024๋…„ 4์›” 29์ผ
0
post-thumbnail

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” ์‹ค์ œ๋กœ ์ผ๋ ‰ํŠธ๋ก  ์‹คํ–‰ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ „ ํฌ์ŠคํŠธ๋ฅผ ๋ณด๊ณ  ์˜ค๋ฉด, ๋” ์ดํ•ด๊ฐ€ ์‰ฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.๐Ÿ˜‰

1. React + electron ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

โญ react ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค

npx create-react-app react-electron-project

โญ ํ”„๋กœ์ ํŠธ์— ์ผ๋ ‰ํŠธ๋กœ ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

npm install --dev electron

โญ package.json์—์„œ electron ๊ด€๋ จ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{
 ...
 "main" : "src/main.js" 
 "scripts" : {
 	...
    "electron" : "electron ."
    }
}

-> ์ผ๋ ‰ํŠธ๋ก ์˜ entry point๋ฅผ main.js๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
-> electron : electron ์€ ์ผ๋ ‰ํŠธ๋ก  ์‹คํ–‰ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

2. main.js ์„ค์ •ํ•˜๊ธฐ

โญ src/main.js๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

const { app, BrowserWindow } = require('electron');
const path = require('path')
function createWindow() {
	const win = new BrowserWindow({
    	width : 800,
        height : 600,
        webPreferences : {
        	nodeIntergration : true,
            contextIsolation : true,
            preload : path.join(__dirname, 'preload.js'),
        },
    })
    win.loadURL('http://localhost:3000')
   }
   app.whenReady().then(() => {
   	createWindow()
   })
   app.on('window-all-closed', function() {
   	if (process.platfrom !=='darwin') app.quit()
})

-> ๋ฆฌ์•กํŠธ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด, win.loadFile์„ win.loadURL('http://loacalhost:3000')๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

3. ํŒจํ‚ค์ง€ ์ถ”๊ฐ€

โญ react์™€ electron์„ ๊ฐ™์ด ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ์•„๋ž˜ ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

npm install --dev concurrently wait-on

-> concurrently : ์ผ๋ ‰ํŠธ๋ก ๊ณผ ๋ฆฌ์•กํŠธ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ‚ค์ง€ ์ž…๋‹ˆ๋‹ค.
-> wait-on : ํ”„๋กœ์„ธ์Šค ๋™์‹œ ์ˆ˜ํ–‰์‹œ ํ•œ๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋‹ค ์™„๋ฃŒ๋œ ํ›„ ๋‹ค์Œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

โญ package.json์˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

  "scripts": {
    "start": "concurrently \"npm:react-start\" \"npm:electron-start\"",
    "react-start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "electron": "electron .",
    "electron-start": "wait-on http://localhost:3000 && electron ."
  },

โญ ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์€ ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ, .env ํŒŒ์ผ ์ƒ์„ฑ ํ›„ BROWSER=none์ด๋ผ๋Š” ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด ์ค๋‹ˆ๋‹ค

์ด์ œ, ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ผ๋ ‰ํŠธ๋ก ์—์„œ ๋ฆฌ์•กํŠธ๋ฅผ ์‹คํ–‰ํ•œ ํ™”๋ฉด์ด ๋‚˜์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝ์‹œ ๋ฐ”๋กœ ๋ฐ˜์˜์ด ๋˜๊ณ , ๋ธŒ๋ผ์šฐ์ € ์ฐฝ๊ณผ ํ•จ๊ป˜ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์— electron์„ ์ถ”๊ฐ€๋กœ ์‹คํ–‰ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ํŽธ๋ฆฌํ•จ์„ ์–ป๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. โ˜บ

4. api ์ฝ”๋“œ ์ž‘์„ฑํ•ด๋ณด๊ธฐ

์ด์ „ ํฌ์ŠคํŠธ์—์„œ ์ •๋ฆฌํ–ˆ๋˜ preload.js๋ฅผ ์‹ค์ œ๋กœ ํ™œ์šฉํ•ด ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค์™€ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ๊ฐ„ api ํ†ต์‹  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ฌ์‹ค, react์™€ electron์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ๊ธฐ์กด์˜ react์™€ ๋‹ฌ๋ผ์ง€๋Š” ์ ์ด ํฌ๊ฒŒ๋Š” ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๋งŒ์•ฝ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ nodejs์˜ ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์™€์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๋ฉด preload ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์ง€๋งŒ ๊ทธ ์ด์™ธ์—๋Š” api ํ†ต์‹ ์„ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๊ณ ์„  ๊ธฐ์กด์˜ react์™€๋Š” ํฌ๊ฒŒ ๋‹ค๋ฅธ ์ ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ์ ์‘ํ•ด ์•ฑ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜‰

  1. signIn api ํ•จ์ˆ˜ ์ƒ์„ฑ
// ipcHandlers.js
const axios = require('axios');

async function handleSignIn(event, {userId, password}) {
	try {
    	const res = await axiosInstance.post('/user/login', {userId, password});
        return res.data;
    } catch (error) {
    	console.error(error)
    }
}

module.exports = {
	handleSignIn
}
  1. main ํ”„๋กœ์„ธ์Šค์— api ํ•จ์ˆ˜ ๋“ฑ๋ก
// main.js
const ipcHandlers = require('./ipcHandlers');

function createWindow() {
	...
    
    app.whenReady().then(() => {
    	createWindow();
        
        ipcMain.handle('signIn', ipcHandlers.handleSignIn); โญ
    });
    
    ...
}
-> 'signIn'์ด๋ผ๋Š” ์ฑ„๋„์— handleSignIn listner๋“ฑ๋ก 
  1. ipcRender๋ฅผ ํ†ตํ•œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ์˜ ๋น„๋™๊ธฐ์  ํ†ต์‹  ํ˜ธ์ถœ
// preload.js 
const { contextBride, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
	signIn : ({ userId, password}) => ipcRenderer.invoke('signIn', {userId, password}),
})

-> 'signIn' ์ฑ„๋„ ํ˜ธ์ถœ 
  1. ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ
// App.tsx
function App() {
	// ์ „์—ญ์ ์œผ๋กœ ์„ ์–ธ๋œ electronAPI ๊ฐ€์ ธ์˜ค๊ธฐ
	const signIn = (window as any).electronAPI.signIn;
    const [accessToken, setAccessToken] = useState();
    
    useEffect(() => {
    	signIn({userId : '1', password : '1'})
        	.then((res) => {
            	setState(res.accessToken);
            })
            .catch((err) => {
            	console.error(err);
            })
   	}, []);
    
    retrn (
    	<>
        	<div>{accessToken}</div>
        </>
    

-> ์ด๋ ‡๊ฒŒ ๋ Œ๋”๋Ÿฌํ”„๋กœ์„ธ์Šค์—์„œ preload์—์„œ ํ˜ธ์ถœํ•œ signIn ์ฑ„๋„์˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

+++ ์ฐธ๊ณ ๋กœ electron์—์„œ๋Š” BrowserRouter๊ฐ€ ์•„๋‹Œ, HashRouter๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค :)

0๊ฐœ์˜ ๋Œ“๊ธ€