ESM 지원
CommonJS로만 개발되던 많은 프로그램들이 ESM을 지원하는 경우가 점점 늘고 있다.
대표적인 예로 몇 가지를 들어보면 google Lighthouse, Express.js , Mongoose (MongoDB ODM)등을 들 수 있다.
국내에서는 카카오테크에서 작성한 CommonJS에서 ESM으로 전환하기를 보면 다음의 문장을 확인할 수 있다.
구글의 Lighthouse 10 버전은 모듈 시스템이 CommonJS에서 ESM으로 전환되었기 때문에 CommonJS의 require() 함수로는 ESM 시스템의 모듈을 불러올 수 없게 되어있다
이런 대세 속에 CommonJS의 근본이라 불리는 Node.js의 경우에도 ESM 모듈을 지원하고 있다.
NodeJS의 경우 11 버전 때까지는 실험적인 준비단계에서 12부터 안정화 단계에 이르렀는데 갓 지원할 당시에는 --experimental-module라는 키워드를 넣고 확장자를 mjs로 바꿔서 실행해야만 되었던 거로 기억한다.
ECMAScript Modules (ESM)는 Common JS 보다는 뒤늦게 나왔지만 JavaScript의 공식 모듈 시스템으로 브라우저 호환성(일관성)이 좋다 보니 현재 시점에서는 CommonJS보다는 ESM 방식을 선호하는 추세이다.
Electron Framework 또한 28 버전 이후로 공식 ESM을 지원하고 있다.
위 문서에도 나와있듯, 기본 Node.js 콘텍스트 위에 ESM 로더를 이용하여 지원하는 것으로 설명되어 있다.
다만 문서 내에 Renderer 부분에서는 Sendbox 옵션을 킨 경우에는 Preload 시 ESM 로더는 지원하지 않는다고 나와있다.
Sendbox Renderer란, 크로미움에서 제공하는 기본 보안을 랩핑이라 생각하면 편한데, 아래 링크를 통해 보안 메커니즘을 확인할 수 있다. https://chromium.googlesource.com/chromium/src/+/main/docs/design/sandbox.md#Overview
Sendboxed preload script 나는 건 결국 sendbox모드 하에서 로드되는 preload Script라는 뜻이고,
preload script란 우리가 기존에 BrowserWindow에서 옵션으로 넣었던 생각하는 preload.js가 맞다.
sendbox는 default가 true이므로 따로 명시하지 않은 이상 기본이 true 이므로 그냥 사용한 사람이라면 기본적으로 sendbox 옵션을 활성화하여 사용했다고 생각해도 무방하다.
sendbox 옵션은 보안 관련 옵션이기에 끄는 건 권장하지 않는다.
우선 여기까지 확인한 대로 어느 부분까지 지원되고 어디까지 지원되지 않는지는 알았으니 세팅에 들어갔다.
환경 세팅
npm init
- npm init을 통해 package.json을 생성
npm init
package.json 편집
- 필요에 따라 나머지 정보는 변경하되, main 은 dist 하위에 시작파일로 지정하자.
- node-fetch는 백엔드단 node에서 fetch를 호출하는 경우가 있는 경우 추가하자.
{
"name": "testProgram",
"version": "0.0.1",
"description": "testProgram",
"main": "./dist/index.js",
"author": "god-logger",
"license": "MIT",
"type": "module",
"devDependencies": {
"electron": "^28.2.0",
"typescript": "^5.0.0",
},
"scripts": {
"start": "tsc && electron .",
},
"dependencies": {
"node-fetch": "^3.3.2"
}
}
dependencies install
# npm install -g yarn 을 통해 yarn을 global로 설치한 경우
yarn install
또는
npm install
typescript init
tsc --init
위 명령어를 통해 tsconfig.json 파일을 생성한 후 아래와 같이 수정한다.
{
"compilerOptions": {
"lib": ["ESNext", "dom"],
"module": "ESNext",
"target": "ES6",
"moduleResolution": "Node",
"outDir": "./dist",
"rootDir": "./",
"noEmitOnError": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
여기서 중요한 건 outDir과 lib에 dom을 추가하는 부분이다.
preload.ts 구현
DomContentLoaded 콜백함수를 일렉트론 공식사이트에 표시된 내용을 타입스크립트화 하여 넣어놨다.
안된다던 esm 방식의 import로 ipcRenderer를 추가했다.
안된다고 했지만 Node.js 버전이나 일렉트론 신규버전에서 갑자기 지원할 수도 있겠단 희망을 가지고 시도해 봤다.
그 외 ipcRenderer와 기타 interface 등을 간단히 정의해 놨다.
이에 따라 IpcMain.ts 작성했다.
ipcMain.ts의 경우와 IpcRenderer.ts의 경우 개인적으로 만드는 프로그램에 사용할 코드라 본 포스팅엔 자세히 서술하지 않고 넘어간다.
index.ts 구현
import { app, BrowserWindow } from "electron";
import { fileURLToPath } from "url";
import path from "path";
import "./ipc/ipcMain.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 900,
height: 750,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
},
});
mainWindow.loadURL("{보여줄 페이지 URL}");
mainWindow.webContents.on("dom-ready", () => {
if (mainWindow) {
mainWindow.setTitle("{타이틀바 제목}");
}
});
};
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
__filename과 __dirname은 기존 cjs에서만 쓰이던 예약어였지만, esm엔 없으므로 동일한 동작을 할 수 있도록 만들었다.
타입스크립트로 작성하지만 결국 컴파일된 이후에 실행되는 파일은 index.js 이기에 import 하는 부분도 ipcMain.ts가 아닌 ipcMain.js로 작성한다.
실행
- 아래 명령어를 통해 실행한다.
yarn start
or
npm start
- 1차 시도
- 프로그램은 실행되지만, preload 가 읽히지 않는다.
공식문서에서 말한 그대로다... esm 방식으로는 읽히지 않는다.
cannot use import state outside a module을 피하고자 preload.js를 수정해 봐도 소용이 없었다.
- 프로그램은 실행되지만, preload 가 읽히지 않는다.
2. 2차 시도
- preload.js 에서 import 방식을 common.js 방식으로 바꿔 실행
- 이상 없이 사용 가능.
결론
개인 사이드 프로젝트에 사용한 ElectronFramework + Typescript + ESM 은 대체적으로 괜찮았다.
본문에는 작성하지 않았지만, IPC 통신을 사용하는데도 큰 무리는 없었고, preload의 경우 그나마 typescript라도 사용할 수 있어 이전보다 코드가 깔끔해졌다.
Typescript 컴파일로 인해 최초 실행 시 시간을 조금 잡아먹긴 해도, 크게 불편함은 없고 기능도 대체적으로 잘 실행되는 걸로 보아 추후에 사내 프로그램도 typescript + esm으로 변경하면 좋을 것 같다는 생각이 들었다.
'자바스크립트 > Electron Framework' 카테고리의 다른 글
ZeroMQ + Electron Framework 앱 실행 불가 이슈 (0) | 2024.11.08 |
---|---|
Electron Builder 로 빌드 시 cannot be closed. Please close it manually and click ret (4) | 2024.10.10 |
[Window] Electron Builder 실행시 cannot create symbolic link 이슈 (4) | 2024.01.31 |
Electron 클라이언트 중복 실행 방지 방법 (0) | 2023.10.06 |