성능 최적화 — 렌더링과 시작 속도 개선
"Electron 앱이 느리다는 편견을 깨려면 최적화가 필수" — 시작 시간, 렌더링 성능, 메모리 사용량을 체계적으로 개선할 수 있습니다.
시작 속도 최적화
1. 지연 로딩
// ❌ 앱 시작 시 모든 모듈을 즉시 로드
const { app, BrowserWindow, Menu, Tray, dialog } = require('electron');
const fs = require('fs');
const path = require('path');
const Database = require('better-sqlite3');
// ✅ 필요할 때 로드 (Lazy Loading)
const { app, BrowserWindow } = require('electron');
let db;
function getDatabase() {
if (!db) {
const Database = require('better-sqlite3');
db = new Database(dbPath);
}
return db;
}
2. show: false + ready-to-show
const win = new BrowserWindow({
show: false, // 즉시 표시하지 않음
});
win.once('ready-to-show', () => {
win.show(); // 콘텐츠가 준비된 후 표시
});
3. 스플래시 스크린
function createSplashScreen() {
const splash = new BrowserWindow({
width: 400,
height: 300,
frame: false,
transparent: true,
alwaysOnTop: true,
});
splash.loadFile('splash.html');
return splash;
}
app.whenReady().then(() => {
const splash = createSplashScreen();
const mainWindow = new BrowserWindow({
show: false,
webPreferences: { preload: path.join(__dirname, 'preload.js') },
});
mainWindow.loadFile('index.html');
mainWindow.once('ready-to-show', () => {
splash.destroy();
mainWindow.show();
});
});
렌더링 성능
1. CSS will-change 활용
/* 애니메이션이 있는 요소에 GPU 가속 */
.animated-panel {
will-change: transform;
}
/* 스크롤 컨테이너 최적화 */
.scroll-container {
contain: layout style paint;
overflow-y: auto;
}
2. 가상 스크롤링
// 수천 개의 아이템을 표시할 때
// 화면에 보이는 아이템만 DOM에 렌더링
class VirtualList {
constructor(container, itemHeight, totalItems, renderItem) {
this.itemHeight = itemHeight;
this.totalItems = totalItems;
this.renderItem = renderItem;
container.style.height = `${itemHeight * totalItems}px`;
container.style.position = 'relative';
this.viewport = container.parentElement;
this.viewport.addEventListener('scroll', () => this.render());
this.container = container;
this.render();
}
render() {
const scrollTop = this.viewport.scrollTop;
const viewportHeight = this.viewport.clientHeight;
const startIdx = Math.floor(scrollTop / this.itemHeight);
const endIdx = Math.min(
startIdx + Math.ceil(viewportHeight / this.itemHeight) + 1,
this.totalItems
);
this.container.innerHTML = '';
for (let i = startIdx; i < endIdx; i++) {
const el = this.renderItem(i);
el.style.position = 'absolute';
el.style.top = `${i * this.itemHeight}px`;
this.container.appendChild(el);
}
}
}
3. requestIdleCallback 활용
// 우선순위가 낮은 작업을 유휴 시간에 실행
function runInIdle(tasks) {
let index = 0;
function processNext(deadline) {
while (index < tasks.length && deadline.timeRemaining() > 5) {
tasks[index]();
index++;
}
if (index < tasks.length) {
requestIdleCallback(processNext);
}
}
requestIdleCallback(processNext);
}
IPC 성능
// ❌ 매번 IPC 호출
for (const item of items) {
await window.electronAPI.saveItem(item);
}
// ✅ 배치로 한 번에 전송
await window.electronAPI.saveItems(items);
성능 측정
// 메인 프로세스 성능 측정
const { performance } = require('perf_hooks');
const startTime = performance.now();
// 작업 수행
const elapsed = performance.now() - startTime;
console.log(`작업 소요 시간: ${elapsed.toFixed(2)}ms`);
// 앱 시작 시간 측정
app.on('ready', () => {
console.log(`앱 준비 시간: ${performance.now().toFixed(0)}ms`);
});
면접 포인트 정리
- 지연 로딩으로 시작 시 필요한 모듈만 로드
show: false+ready-to-show로 백화현상 방지- 대량 데이터는 가상 스크롤링으로 DOM 부담 최소화
- IPC 호출은 배치로 묶어서 오버헤드 절감
performance.now()로 병목 구간 측정
성능 최적화를 다뤘으면, 다음은 메모리 관리를 알아봅시다.
댓글 로딩 중...