"에디터, IDE, 브라우저 같은 앱은 여러 윈도우를 동시에 관리해야 한다" — 윈도우 생성, 추적, 상태 동기화, 정리까지 체계적인 관리가 필요합니다.


윈도우 매니저 패턴

JAVASCRIPT
class WindowManager {
  constructor() {
    this.windows = new Map();
    this.nextId = 1;
  }

  create(options = {}) {
    const id = this.nextId++;
    const win = new BrowserWindow({
      width: options.width || 800,
      height: options.height || 600,
      show: false,
      webPreferences: {
        preload: path.join(__dirname, 'preload.js'),
        contextIsolation: true,
      },
      ...options,
    });

    win.once('ready-to-show', () => win.show());

    win.on('closed', () => {
      this.windows.delete(id);
      this.broadcastWindowList();
    });

    this.windows.set(id, { win, metadata: options.metadata || {} });
    win.loadFile(options.file || 'index.html');

    this.broadcastWindowList();
    return { id, win };
  }

  get(id) {
    return this.windows.get(id)?.win;
  }

  getAll() {
    return Array.from(this.windows.entries()).map(([id, { win, metadata }]) => ({
      id,
      title: win.getTitle(),
      focused: win.isFocused(),
      ...metadata,
    }));
  }

  close(id) {
    const entry = this.windows.get(id);
    if (entry) entry.win.close();
  }

  closeAll() {
    for (const { win } of this.windows.values()) {
      win.close();
    }
  }

  focusWindow(id) {
    const entry = this.windows.get(id);
    if (entry) {
      const win = entry.win;
      if (win.isMinimized()) win.restore();
      win.focus();
    }
  }

  // 모든 윈도우에 윈도우 목록 업데이트 전송
  broadcastWindowList() {
    const list = this.getAll();
    for (const { win } of this.windows.values()) {
      if (!win.isDestroyed()) {
        win.webContents.send('windows:list', list);
      }
    }
  }

  // 특정 윈도우를 제외한 모든 윈도우에 메시지 전송
  broadcast(channel, data, excludeId) {
    for (const [id, { win }] of this.windows) {
      if (id !== excludeId && !win.isDestroyed()) {
        win.webContents.send(channel, data);
      }
    }
  }
}

const windowManager = new WindowManager();

IPC 연동

JAVASCRIPT
// main.js
ipcMain.handle('window:create', (_event, options) => {
  const { id } = windowManager.create(options);
  return id;
});

ipcMain.handle('window:list', () => {
  return windowManager.getAll();
});

ipcMain.on('window:close', (_event, id) => {
  windowManager.close(id);
});

ipcMain.on('window:focus', (_event, id) => {
  windowManager.focusWindow(id);
});

// 모든 윈도우에 데이터 브로드캐스트
ipcMain.on('broadcast', (event, channel, data) => {
  const senderWin = BrowserWindow.fromWebContents(event.sender);
  // 보낸 윈도우를 제외하고 전송
  const senderId = Array.from(windowManager.windows.entries())
    .find(([_, { win }]) => win === senderWin)?.[0];
  windowManager.broadcast(channel, data, senderId);
});

윈도우 타입별 관리

JAVASCRIPT
// 다른 종류의 윈도우 생성
function createEditorWindow(filePath) {
  return windowManager.create({
    file: 'editor.html',
    metadata: { type: 'editor', filePath },
    width: 1200,
    height: 800,
  });
}

function createSettingsWindow() {
  // 설정 창은 하나만 허용
  const existing = windowManager.getAll().find(w => w.type === 'settings');
  if (existing) {
    windowManager.focusWindow(existing.id);
    return;
  }

  return windowManager.create({
    file: 'settings.html',
    metadata: { type: 'settings' },
    width: 600,
    height: 400,
    resizable: false,
  });
}

function createPreviewWindow(parentId) {
  const parent = windowManager.get(parentId);
  return windowManager.create({
    file: 'preview.html',
    metadata: { type: 'preview', parentId },
    parent: parent,
    width: 600,
    height: 800,
  });
}

윈도우 간 상태 동기화

JAVASCRIPT
// 테마 변경 시 모든 윈도우에 반영
ipcMain.on('theme:change', (_event, theme) => {
  store.set('theme', theme);
  // 모든 윈도우에 브로드캐스트
  windowManager.broadcast('theme:changed', theme);
});

면접 포인트 정리

  • 윈도우 매니저 패턴으로 다중 윈도우의 생성/추적/정리를 중앙 관리
  • broadcast 패턴으로 윈도우 간 상태 동기화
  • 설정 창처럼 한 개만 허용해야 하는 윈도우는 중복 생성 방지 필요
  • 윈도우가 닫힐 때 Map에서 참조를 제거하여 메모리 누수 방지

다중 윈도우를 다뤘으면, 다음은 프레임리스 윈도우를 알아봅시다.

댓글 로딩 중...