"Electron 앱이 느리다는 편견을 깨려면 최적화가 필수" — 시작 시간, 렌더링 성능, 메모리 사용량을 체계적으로 개선할 수 있습니다.


시작 속도 최적화

1. 지연 로딩

JAVASCRIPT
// ❌ 앱 시작 시 모든 모듈을 즉시 로드
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

JAVASCRIPT
const win = new BrowserWindow({
  show: false,  // 즉시 표시하지 않음
});

win.once('ready-to-show', () => {
  win.show();  // 콘텐츠가 준비된 후 표시
});

3. 스플래시 스크린

JAVASCRIPT
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 활용

CSS
/* 애니메이션이 있는 요소에 GPU 가속 */
.animated-panel {
  will-change: transform;
}

/* 스크롤 컨테이너 최적화 */
.scroll-container {
  contain: layout style paint;
  overflow-y: auto;
}

2. 가상 스크롤링

JAVASCRIPT
// 수천 개의 아이템을 표시할 때
// 화면에 보이는 아이템만 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 활용

JAVASCRIPT
// 우선순위가 낮은 작업을 유휴 시간에 실행
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 성능

JAVASCRIPT
// ❌ 매번 IPC 호출
for (const item of items) {
  await window.electronAPI.saveItem(item);
}

// ✅ 배치로 한 번에 전송
await window.electronAPI.saveItems(items);

성능 측정

JAVASCRIPT
// 메인 프로세스 성능 측정
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()로 병목 구간 측정

성능 최적화를 다뤘으면, 다음은 메모리 관리를 알아봅시다.

댓글 로딩 중...