모듈 로드 최적화
문제점
불필요하거나 무거운 Node.js 모듈을 초기 로드하면 메모리와 초기화 시간이 증가합니다.
전략
- 필요한 모듈만 선택적으로 로드
- 동적 임포트(dynamic import)로 필요 시점에 모듈을 가져옴
// Bad Example: 큰 모듈을 전역으로 require
const heavyModule = require('heavy-module'); // 이 모듈은 수백 MB의 데이터를 포함함
// Good Example: 필요할 때 동적으로 로드 (Lazy Loading)
async function loadHeavyModule() {
return await import('heavy-module');
}
// 사용 시점에 모듈 로드
loadHeavyModule().then(module => {
module.doHeavyTask();
});
초기화 지연 처리
문제점
앱 시작 시 모든 작업을 동시에 실행하면 초기 로딩 속도가 느려집니다.
전략
- 필수 작업만 초기화하고, 나머지는 지연 로드(lazy loading)
- 사용자 인터랙션에 따라 필요한 리소스를 로드
// parser.js: 앱 시작 시 바로 파일 목록을 읽지 않고, 함수 호출 시 읽도록 수정
const fs = require('fs');
class Parser {
async getFiles() {
if (!this.files) {
this.files = await fs.promises.readdir('./data');
}
return this.files;
}
async parseFiles() {
const files = await this.getFiles();
// 여기서 동적으로 heavy parsing 모듈을 로드합니다.
const parserModule = await import('light-parser');
return parserModule.parse(files);
}
}
module.exports = new Parser();
메인 프로세스 차단 방지
문제점
메인 프로세스(컨트롤 타워)는 사용자 입력, 창 관리, IPC 등 핵심 작업을 수행합니다. 긴 작업이나 동기 작업이 이 프로세스를 차단하면 앱 전체가 멈추게 됩니다.
전략
- 무거운 작업을 워커 스레드나 별도 프로세스로 분리
- 비동기 API 사용, 동기 IPC 회피
// CPU 집약적 작업은 Worker Thread로 처리
const { Worker } = require('worker_threads');
ipcMain.on("start-heavy-task", (event, data) => {
const worker = new Worker(
path.join(__dirname, "worker.js"),
{
workerData: data,
},
);
worker.on("message", (result) => {
event.sender.send("heavy-task-result", result);
});
worker.on("error", (error) => {
event.sender.send("heavy-task-error", error.message);
});
});
// worker.js (별도 파일)
const { workerData, parentPort } = require('worker_threads');
// 무거운 계산 작업 수행
const result = workerData.map(num => num * 2);
parentPort?.postMessage(result);
// renderer.js
const { ipcRenderer } = require("electron");
document.getElementById("start-button").addEventListener("click", () => {
const data = [1, 2, 3, 4, 5];
ipcRenderer.send("start-heavy-task", data);
});
ipcRenderer.on("heavy-task-result", (event, result) => {
document.getElementById("result").textContent = `결과: ${result}`;
});
렌더러 프로세스 최적화
문제점
렌더러 프로세스에서는 UI 업데이트, 애니메이션, 스크롤 등 사용자가 직접 경험하는 부분이 동작합니다. 이곳에 긴 작업이 들어가면 앱이 멈추거나 느려집니다.
전략
- requestIdleCallback(): 브라우저의 idle 상태에 호출될 함수를 대기열에 넣습니다.
- requestAnimationFrame(): 브라우저에게 수행하기를 원하는 애니메이션을 알립니다.
- Web Workers: 무거운 계산이나 데이터 처리 작업은 별도의 워커 스레드로 분리합니다.
// requestIdleCallback 사용
// renderer.js
function lowPriorityTask() {
console.log('낮은 우선순위 작업 실행');
// 작업 내용
}
if (window.requestIdleCallback) {
requestIdleCallback(lowPriorityTask);
} else {
// 지원하지 않는 경우 setTimeout 사용
setTimeout(lowPriorityTask, 200);
}
// Web Worker 사용
// worker.js
self.onmessage = (e) => {
const result = e.data.map(item => item * 2); // 예시 작업
self.postMessage(result);
};
// renderer.js
const worker = new Worker('worker.js');
worker.onmessage = (e) => {
console.log('Worker 결과:', e.data);
};
worker.postMessage([1, 2, 3, 4, 5]);
네트워크 및 리소스 관리
문제점
불필요한 네트워크 요청이나 폴리필은 초기 로드 시간을 늘립니다.
전략
- 앱 번들에 포함할 수 있는 리소스는 미리 다운로드하여 포함합니다.
- 필요 없는 폴리필은 제거하고, 최신 ECMAScript를 사용합니다.
- 네트워크 요청이 필수적이라면 캐싱 전략을 도입합니다.
코드 번들링으로 로드 속도 개선
문제점
여러 파일로 나누어 require() 호출을 반복하면, 로딩 시간과 메모리 사용량이 증가할 수 있습니다.
전략
Webpack, Parcel, Rollup.js와 같은 번들러를 사용하여 모든 JavaScript 코드를 하나의 파일로 번들링하면 초기 require 호출 오버헤드를 줄일 수 있습니다.
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/main.js',
target: 'electron-main',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'production'
};
불필요한 기본 메뉴 제거
Electron은 기본 메뉴를 생성하는데, 필요하지 않은 경우 이를 제거하면 약간의 성능 향상을 기대할 수 있습니다.
// main.js
const { app, BrowserWindow, Menu } = require('electron');
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 800,
height: 600
});
// 불필요한 기본 메뉴 제거
Menu.setApplicationMenu(null);
win.loadFile('index.html');
});
'Electron' 카테고리의 다른 글
[Electron] 키보드 단축키 구현 (0) | 2025.04.07 |
---|---|
[Electron] 보안 강화 (0) | 2025.04.07 |
[Electron] MessagePorts: 메시지 채널을 통한 프로세스 간 직접 통신 (0) | 2025.03.24 |
[Electron] 프로세스 샌드박싱: 보안을 위한 실행 격리 (0) | 2025.03.24 |
[Electron] IPC: 프로세스 간 통신으로 앱 기능 확장하기 (0) | 2025.03.24 |