돌아온 앱 만들기 시리즈

 

바이트가 아니라 빗! 이라고 강조하더군요


링크

① 데스크탑 앱 만들기 #1 - Vite2 + Vue3 + Electron

② 데스크탑 앱 만들기 #2 - Hot reload

③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer(예정)

 

 데스크탑 앱 만들기 시리즈를 작년 9월에 시작하고 벌써 1년이 넘게 지났네요. 그동안 많은 기술이 나왔고 일렉트론도 엄청난 버전업을 했습니다. 2021년 10월 13일 오전 1시 30분 현재 15.1.2 버전이군요. 작년 이맘때가 10.1.0 버전이었는데.(얘네는 왜 이렇게 빨리 버전을 올리는지 모르겠습니다)

 

 그리고 바로 몇 시간 전 11시에 Nuxt3가 또 발표됐습니다. 두근대는 마음으로 만지작 거리다가 오랜만에 블로그를 업데이트해야겠다는 생각이 들어서 바로 시작합니다! 조만간  Nuxt에 대한 내용도 포스팅하고 싶군요 두근두근.

 

 아무튼 그 동안 Vue3도 나오고 Vite도 2 버전으로 업그레이드되었습니다. Vite를 처음 접했을 때의 그 놀라움을 아직도 잊지 못하고 있습니다. 항상 VueCLI로만 작업을 하던 저에게 새로고침 속도의 미학을 알려주었죠. (감동)

에반유. 당신은 정말..

그래서 여러분께도 그 감동을 전해드리고 싶었어요. 자 그럼 각설하고 바로 앱 만들기 시리즈 첫번째. 환경설정에 들어가 보도록 하겠습니다.


1. Vite 설치

npm init vite@latest
// or
yarn create vite

 뒤에 프로젝트 이름을 넣을 수 있는 데 그냥 바로 실행하면 아래 처럼 이름과 옵션을 고를 수 있어요.

~$ yarn create vite

yarn create v1.22.15
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-vite@2.6.6" with binaries:
      - create-vite
      - cva
✔ Project name: … vite-electron-test
? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
❯   vue
    react
    preact
    lit
    svelte

타입스크립트는 vue-ts를 골라주면 되는데 일단 저는 vue 선택해서 Javascript로 진행합니다.

✔ Project name: … vite-electron-test
✔ Select a framework: › vue
? Select a variant: › - Use arrow-keys. Return to submit.
❯   vue
    vue-ts

그러면 완료. 들어가서 종속성을 설치하고 실행합니다.

Done. Now run:

  cd vite-electron-test
  yarn
  yarn dev

✨  Done in 262.96s.

그리고 yarn dev 또는 npm run dev 하면

짜장

라라벨 Watch나 VueCLI serve를 쓰다 보니 속도 하나에도 감동을 느낍니다. 물론 쟤네가 느린 건 아니지만..

localhost:3000만큼 사랑해

완성된 첫페이지입니다.

 

그리고 폴더구조는 아래와 같아요.

아무것도 없지요

아무것도 없어요. 이제부터 하나하나 설치해봅니다.

 

2. Electron 설치

 바로  Dev 디펜던시에 추가해줍니다.

npm install --save-dev electron
//or
yarn add -D electron

설치한 결과  package.json

{
  "name": "vite-electron-test",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "vue": "^3.2.16"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.9.3",
    "electron": "^15.1.2",
    "vite": "^2.6.4"
  }
}

자 이제 설치했으니 다음 단계로!

 

3. Vite 빌드

 Vite를 빌드하면 dist 폴더에 빌드 파일이 생성됩니다. 그걸 Electron이 읽을 수 있도록 만들어볼 거예요.

npm run build
//or
yarn build

빠른빌드

빌드를 하면 dist폴더에 static파일들이 생성된 것을 확인할 수 있습니다. 이제 Electron이 얘를 읽을 수 있게 경로 설정할 거예요.

 

다음은 Electron 설정을 시작해보죠!

 

4. Electron 파일 설정

Electron 빠른 시작 페이지 맨 밑에 보면 코드가 싹 다 나와있습니다.

링크: https://www.electronjs.org/docs/latest/tutorial/quick-start 

 

네가지 파일 정도를 신경써줘야합니다. 실제로는 3개파일이예요.

- 1. main.js

- 2. preload.js

- 3. index.html(있는거 그대로)

- 4. package.json

 

저 중에 2.preload.js는 세 번째 시리즈(③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer)에서 통신을 위해 필요하니까 파일만 만들면 두시면 됩니다. 그리고 index.html은 그냥 가만히 냅두면 되고 package.json파일은 만들 필요 없으니 main.js파일 경로랑 "start"스크립트만 추가하면 됩니다.

 

package.json

{
  "name": "vite-electron-test",
  "version": "0.0.0",
  "main": "main.js", // <-- 요기 추가
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview",
    "start": "electron ." // <-- 요기 추가
  },
  "dependencies": {
    "vue": "^3.2.16"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.9.3",
    "electron": "^15.1.2",
    "vite": "^2.6.4"
  }
}

main.js 파일명이나 script에 "start" 명령은 좋아하는 걸로 바꿔주세요. 편하신 걸로!

 

main.js 생성

const { app, BrowserWindow } = require('electron')
const path = require('path')

function createWindow () {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('./dist/index.html') // <-- 요기에 './dist/'를 추가!

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

 

preload.js 파일 생성

// 냉무

 

자 끝났습니다. 이제 시작해볼까요?

 

5. 실행 그리고 오류 고치기

 설정은 대략 끝났으니 실행해봅니다.

npm run start
//or 
yarn start

짜잔! 빈 화면이 보입니다. 그리고 콘솔 창에 이런 오류가 있을 거예요.

뭐라는 거야 대체.

dist안에 있는 index.html 파일을 열어봅니다.

 

파일들의 경로가

/assets/index.860ec699.js

이런 식으로 지정되어 있을 거예요. Electron은 root폴더에서 실행이 되는데 asset폴더는 root폴더에 있는 게 아니란 말이죠. 그래서 저 경로들을 모조리 dist폴더를 향하게 만들 겁니다.

 

vite.config.js로 가볼게요.

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
const path = require("path"); // <-- 얘 추가하고

// https://vitejs.dev/config/
export default defineConfig({
  base: path.resolve(__dirname, "dist"), // <-- 얘 추가하고
  plugins: [vue()],
});

저 두 줄을 추가하고 다시 빌드합니다.

 

그리고 다시 dist폴더 안에 index.html 파일에 소스 경로를 확인해보면

/Users/my-name/electron/vite-electron-test/dist/assets/index.860ec699.js

 

요로케 뭔가 길어진 것을 확인할 수 있습니다. 그러니 이제 root에 있는 main.js가 읽어 들일 수 있게 변했군요.

 

그럼 다시 "start"해봅시다.

깔끔!

 

6. 마무리

 끝났습니다. 이제 Vite랑 Vue3랑 Electron을 실행할 준비가 됐어요. 근데 개발을 하려면 소스코드가 변경되었을 때 알아서 바뀌게 하고 싶잖아요? 매번 코드 바뀔 때마다 빌드하고 "start"해주는 건 너무 번거롭잖아요? 그래서 다음 포스팅은  Hot reload기능을 구현해보도록 하겠습니다!

 

그럼 다음 시리즈에서!

 


너무 깔끔해.. 젠장, 기분 좋아.
데스크탑 앱 만들기 마지막 포스팅


링크

① 데스크탑 앱 만들기 #1 - Vue CLI + Electron

② 데스크탑 앱 만들기 #2 - ExcelJS, SheetJS, js-xlsx, js-xlsx-style

③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer

④ 데스크탑 앱 만들기 #4 - ExcelJS, ipcRenderer, ipcMain, removeListener

 

 그럼 이번 포스팅에서는 ipc 통신을 이용해서 frontend(vue)와 backend(electron) 사이에서 데이터를 교환해보겠습니다. 그리고 그 데이터는 시스템에 저장되어있는 엑셀 파일을 ExcelJS로 변환해서 가공해볼게요.(코드에 문제가 있거나 더 좋은 방법 아시는 분은 친절하게 댓글 남겨주세요!)

 

 순서는 다음과 같습니다.

1. frontend에서 엑셀 파일 로드를 요청한다.

2. backend에서 엑셀 파일을 로드한다. ExcelJS 라이브러리를 이용해서 데이터를 가공한다. 가공한 데이터를 frontend로 보낸다.

3. frontend에서 데이터를 갖고 논다.

4. backend로 갖고 놀았던 데이터를 보내 저장할 것을 요청한다.

5. 받은 데이터를 엑셀에 덮어 씌운다.

6. 끝.

 

 하나씩 시작해보겠습니다.

1. frontend에서 엑셀파일 로드를 요청한다.

App.vue

let filePath = '/Users/username/excel/test.xlsx'

ipcRenderer.send('load-excel-files', filePath)

 이 코드는 mounted 또는 created에 넣어서 frontend page가 로드되면 바로 실행되게 했습니다. 그리고 filePath는 엑셀 파일이 있는 위치를 적어줍니다. 저는 맥을 사용하고 있으니 /Users가 시작이고, 윈도우 유저라면 C:\로 시작하겠지요. 그리고 send 메소드를 통해 backend로 파일 경로만 패스해줍니다.

 

2. backend에서 엑셀 파일을 로드한다. ExcelJS 라이브러리를 이용해서 데이터를 가공한다. 가공한 데이터를 frontend로 보낸다.

background.js

ipcMain.on('load-excel-files', async (event, filePath) => {
  const sheetData = []
  const workbook = new ExcelJS.Workbook()
  await workbook.xlsx.readFile(filePath)
  const worksheet = workbook.worksheets[0] // 첫번째 sheet 선택

  const option = { includeEmpty: true }

  await worksheet.eachRow(option, (row, rowNum) => {
    sheetData[rowNum] = []
    row.eachCell(option, (cell, cellNum) => {
      sheetData[rowNum][cellNum] = { value:cell.value, style:cell.style }
    })
  })

  event.reply('reply-excel-files', sheetData)
})

 backend에서 ipcMain.on 메소를 통해 데이터를 받습니다. 그리고 ExcelJS 라이브러리를 이용해서 각 셀을 순회합니다.

 

 eachRow는 각 행을 순회하는 메소드입니다. 첫 번째 줄부터 한 칸씩 아래 방향으로 순회합니다.

 eachCell은 오른쪽 방향으로 각 cell을 순회합니다.

 이제 값을 얻어옵니다. 저 같은 경우에는 cell값과 style만 필요하니까 두 가지만 받아왔어요.

 

 값을 얻어왔으면 다시 frontend로 넘겨줍니다. event 인자에 reply 메소드를 통하면 바로 보내는 것이 가능하다고 합니다.

 

App.vue

ipcRenderer.on('reply-excel-files', (event, sheetData) => {
	console.log(sheetData)
})

 위 코드는 created에 넣어주셔야 위 event.reply 메소드에 반응해서 frontend console창에 sheetData를 출력해줍니다.

 

 

3. frontend에서 데이터를 갖고 논다.

App.vue

function deepClone(obj) {
    if(obj === null || typeof obj !== 'object') {
        return obj;
    }
    const result = Array.isArray(obj) ? [] : {};
    for(let key of Object.keys(obj)) {
        result[key] = deepClone(obj[key])
    }
    return result;
}

myCell.style = await deepClone(mySheetData[7][2].style)
myCell.style.fill = {
	fgColor: {argb: "09B050"},
	pattern: "solid",
	type: "pattern"
}
myCell.value = "myCellValue"

  이제 데이터를 마음껏 가지고 놀면 됩니다.

 

 가지고 놀 때 실제로 해보면서 알게 된 점은 cell에 있는 value는 값이 잘 적용이 되는데 style은 잘 적용이 안되더라구요. 그래서 ExcelJS를 통해 데이터를 읽을 때 가지고 왔던 임의의 원본 cell을 deepClone 해서 style을 바꾸니 잘 되었습니다. 코드에 deepClone 코드도 추가해 놓았으니 참고하세요.(나중에 기회가 생기면 clone에 대한 내용도 쓰고 싶군요!)

 

 fgColor는 color HEX 값인데 앞에 "#"을 지워서 입력하면 잘 작동합니다.

 

4. backend로 갖고 놀았던 데이터를 보내 저장할 것을 요청한다.

App.vue

ipcRenderer.send('write-excel-files', filePath, sheetData)

 

 잘 가지고 놀았던 데이터 뭉치를 ipcRenderer.send 메소드를 통해 전달합니다. 어디에 저장할지도 전달해 주어야 하니 1번에서 사용되었던 filePath('/Users/username/excel/test.xlsx')를 인자로 전달하고, 데이터(sheetData)도 인자로 전달합니다.

 

5. 받은 데이터를 엑셀에 덮어 씌운다.

background.js

ipcMain.on('write-excel-files', async (event, filePath, sheetData) => {
  const workbook = new ExcelJS.Workbook()
  await workbook.xlsx.readFile(filePath)
  const worksheet = workbook.worksheets[0]

  for (let rowNum=1; rowNum<sheetData.length; rowNum++) {
    let row = worksheet.getRow(rowNum)
    for (let cellNum=1; cellNum<sheetData[rowNum].length; cellNum++) {
      let cell = row.getCell(cellNum)
      if ( cell ) {
        cell.value = sheetData[rowNum][cellNum].value
        cell.style = sheetData[rowNum][cellNum].style
      }
    }
  }

  workbook.xlsx.writeFile(filePath).then( ()=>{
    event.reply('reply-excel-files', sheetData)
  }).catch( (error) => {
    event.reply('reply-excel-files', error)
  })
})

 제가 선택한 방법은 기존의 엑셀 파일을 불러와서 row(행)와 cell을 순회하면서 저희가 만지작 거렸던 데이터 뭉치에 있는 value와 style값을 집어넣는 것이었습니다.

 

※ 주의 : 제가 쓰는 방법을 쓰게 되면 문제가 생길 수 있습니다. 예를 들어, frontend에서 데이터를 만지작 거릴 때 프로그래밍 오류로 인해 값이 전부 사라지게 되고, 확인 하지 않고 기존 파일에 덮어 씌우면 기존 데이터들이 다 날아가 버리는 신비를 확인할 수 있습니다.

두 가지를 기억해 주세요.

1. 엑셀 파일을 작업하기 전에 다른 곳에 파일을 복사해 놓는다. 또는 자동으로 복사해 놓는 코딩을 한다.

2. 만지작 거린 데이터는 데이터가 잘 살아있는지 확인하는 코드를 집어넣는다.

 

 잘 입력이 되면 다시 frontend로 reply 합니다. 1번에서 미리 만들어 놓은 코드가 있어서 또 작성할 필요는 없습니다. 1석 2조.

 

6. 끝.

 자 이렇게 되면 과정이 끝나게 됩니다. 그런데 frontend page가 로드될 때 한 번만 불러와져야 할 엑셀 파일이 page가 로드될 때마다 중첩이 되더라구요. 예를 들면 한번 열면 1, 두 번째 열면 2. 이런 식으로 로드되는 횟수만큼 통신이 이루어져서 고치는 방법을 찾게 되었습니다.

 

 위에 보면 2, 3번씩 엑셀 파일 로딩 확인이 되는 것을 볼 수 있습니다.(실제로 로딩을 3번 하는 건 아니고 'reply-excel-files'의 중첩) 프로그램을 완전히 다시 로드(Ctrl + R 또는 ⌘ + R)하면 다시 초기화되는데 라우팅을 설정해서 입장 퇴장을 반복하면 점점 더 쌓이게 됩니다. 이유는 페이지가 로드될 때마다 저희가 1번에서 설정해 놓은 ipcRenderer.on 메소드로 만들어놓은 listener가 중첩되는 현상 같아서 방법을 좀 찾아본 결과 수정하는 데 성공했습니다.

 

App.vue

const listener = ipcRenderer.rawListeners('reply-excel-files')
if  ( listener.length > 1)
	ipcRenderer.removeListener('reply-excel-files', listener[1])

 

 저 같은 경우에는 for문을 이용해서 엑셀 파일을 여러 개 호출하기 때문에 ipcRenderer.on 메소드를 이용하고 Listener가 늘어나면 삭제하는 방법을 사용했지만, 엑셀 파일을 한 개만 호출할 경우 ipcRenderer.on이 아닌 ipcRenderer.once 메소드를 이용하면 Listener가 중첩되지 않는 것을 확인했습니다.

 

 이렇게 프로그램 만들어 보았습니다. 엑셀 파일은 업무에서 많이 사용하고 여러분들도 반복적으로 하는 일이 있을 것이라고 생각합니다. 제 경험이 누군가에게 도움이 되길 바래요. 한번 프로그램을 만들어 놓으면 누구보다 빠르게 업무를 처리할 수 있으니(빠르게 퇴근한다고는 안 했습니다.) 아직 업무 중인 척 연기하시면서 여유로운 회사생활되시기를 바랄게요.

 


프로그램 만드는데 시간은 들어가지만 재밌군요.

 

생소해서 어려운. 하지만 의외로 쉬운.

 

너 거기있고, 나 여기있고.

 

 


링크

데스크탑 앱 만들기 #1 - Vue CLI + Electron

데스크탑 앱 만들기 #2 - ExcelJS, SheetJS, js-xlsx, js-xlsx-style

데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer

④ 데스크탑 앱 만들기 #4 - ExcelJS, ipcRenderer, ipcMain, removeListener

 

 위 포스팅에서 Vue CLI를 이용해서 frontend(renderer)를 만들고, electron-builder를 이용해서 backend(Main)를 만들었습니다. 그리고 excel 파일을 읽는 라이브러리를 ExcelJS를 이용해서 로딩에 성공하였으니 이제 이 객체를 frontend로 보내줘야 데이터를 가지고 이것저것 만지작 거릴 수 있겠죠? 그 이후에 만지작 거린 객체를 다시 백엔드로 보내서 엑셀 파일에 저장해주면 제 프로젝트가 성공하게 됩니다.

 

 아, 그 전에 애초에 frontend에서 파일을 읽고 바로 수정하면 되지 않냐 생각도 했었는데 시스템 안에 있는 파일을 직접 읽고 쓰기 위해서는 일렉트론 같은 시스템 프로그램이 필요하더라구요. 시스템 프로그램이 없으면 프로젝트 로컬 폴더 안에 있는 내용만 접근 가능하고, 폴더 밖에 있는 파일들은 접근이 안되니 업로드하는 방식으로 밖에 없는 것 같았습니다. 애초에 기획이 자동화니까 일렉트론을 쓰기로 결정했었죠.


 그럼 이제 본격적으로 fontend와 backend의 데이터 교환 방법을 알아보겠습니다.

 

 일렉트론 공식 문서에 뭐라고 설명되어있는지 부터 확인하고 갈게요!

 

★ ipcRenderer - renderer 프로세스에서 main 프로세스로 비동기적인 통신을 합니다.

★ ipcMain - main 프로세스에서 renderer 프로세스들로 비동기적인 통신을 합니다.

 

 내용만 봐서는 뭔지 정확히 감이 오질 않습니다. 혹시 Javascript 좀 해보신 분들은 좀 익숙하실지도 모르겠습니다. 웹페이지에서 event emit, listen과 많이 닮아 있다고 생각이 들었거든요.

 

 그럼 공식 문서에 나와있는 테스트 코드를 확인해봅니다.

 

 다음 코드는 frontend에 있는 아무 파일에 넣으면 되는데 저는 App.vue 파일에 넣어서 테스트를 해보겠습니다.

// renderer 프로세스(웹 페이지)안에서
const { ipcRenderer } = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // "pong"이 출력됩니다.

ipcRenderer.on('asynchronous-reply', (event, arg) => {
  console.log(arg) // "pong"이 출력됩니다.
})
ipcRenderer.send('asynchronous-message', 'ping')

 

 ipcRenderer 테스트 코드에서 사용된 메소드는 on, send, sendSync로 3가지입니다.

- on: backend에서 frontend로 통신을 시도하면 받아 주는 메소드

- send: frontend에서 backend로 통신을 보내는 메소드

- sendSync: send메소드는 비동기적으로 보내지고, 이 메소드는 동기적으로 작동한다(동기, 비동기는 여기서 설명하기엔 내용이 복잡해지므로 패스!).

 

 아래 코드가 backend 파일인 background.js에 들어갑니다.

// main 프로세스안에서
const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // "ping" 출력
  event.reply('asynchronous-reply', 'pong')
})

ipcMain.on('synchronous-message', (event, arg) => {
  console.log(arg) // "ping" 출력
  event.returnValue = 'pong'
})

 ipcMain 테스트 코드에서 사용된 메소드는 on, reply로 2가지입니다.

- on: frontend에서 background.js로 통신을 요청하면 받는 주는 메소드

- reply: on이 성공하면 frontend로 다시 통신을 넣어주는 메소드

 

 위의 테스트 코드를 실행했을 때의 결과는 다음과 같습니다.

 

▶︎ frontend(renderer) side

▶︎ backend(main) side

 

 frontend에서 pong 두 번, backend에서 ping 두번이 로그에 출력이 되었네요. 보내고 받고를 두번 했다는 뜻인데 코드를 한번 살펴볼게요.

 빨간색은 비동기(asychronous) 통신을 나타내고, 파란색은 동기(synchronous) 통신을 나타냅니다. 두 가지의 통신을 테스트하는 코드였네요. 그래서 frontend와 backend 콘솔에 pong이 2개, ping이 2개가 출력된 것입니다.

 

 자세히 좀 볼까요?

 

▶︎ 비동기 통신(분홍색, asychronous)

① frontend에서 ipcRenderer.send 메소드를 'asynchronous-message' 채널을 통해 'ping'이라는 데이터를 backend로 보냅니다.

② backend에서 ipcMain.on 메소드에서 정의한 'asynchronous-message' 채널을 통해서 인자를 받고 콘솔에 출력하네요.

그리고 바로 ipcMain.reply 메소드를 'asychronous-reply' 채널을 통해 'pong'이라는 데이터를 frontend로 보냅니다.

④ 마지막으로 frontend에서 ipcRenderer.on 메소드를 통해 정의된 'asychronous-reply' 채널을 통해 받아진 'pong'이라는 데이터는 콘솔에 출력되는 것으로 끝이 납니다.

 

▶︎ 동기 통신(파란색, sychronous)

① frontend에서 ipcRenderer.sendSync 메소드를 'synchronous-message' 채널을 통해 'ping'이라는 데이터를 backend로 보냅니다.

② backend에서 ipcMain.on 메소드에서 정의한 'synchronous-message' 채널을 통해서 인자를 받고 콘솔에 출력하네요.

③ 그리고 event의 returnValue를 'pong'으로 입력하면서 자동적으로 frontend로 데이터가 리턴됩니다.

④ 마지막으로 frontend로 ipcRenderer.sendSync의 리턴 값이 'pong'으로 전달되었고 바로 콘솔에 출력됩니다.

 

 

 어떤가요? 일렉트론의 frontend와 backend이 통신을 어떻게 하는지 같이 살펴보았는데 이해가 되셨으면 좋겠네요. 저는 테스트 코드를 해보면서 생각보다 쉽다는 생각을 했습니다. ipc라는 용어 자체가 헷갈릴 뿐이죠.

 

 그럼 다음 포스팅에서는 제 프로젝트에서 ExcelJS와 ipcMain, ipcRenderer를 이용해서 어떻게 구현하였는지, 그리고 Listner가 중첩돼서 중복적으로 통신 이벤트가 발생하는 현상을 수정하는 방법들을 적어보도록 하겠습니다.

 

 

※ 동기와 비동기의 차이점은 생활코딩님께서 영상으로 너무 잘 설명해주셔서 링크를 남깁니다. 영상은 개념 설명이고 아래 더보기에 강좌 링크는 남겨놓았으니 동기, 비동기가 궁금하신 분은 참고해주세요!

 

생활코딩 - 동기와 비동기의 개념

 


다음 : 데스크탑 앱 만들기 #4 - ExcelJS, ipcRenderer, ipcMain, removeListener

더보기 : 생활코딩 - nodejs에서의 동기, 비동기, 콜백

프로젝트가 끝나갈 무렵, 코딩은 참 즐겁다는 생각이 들었다.

 

+ Recent posts