Electron 빌드에서 전체화면이 적용되지 않을 때 점검할 항목

Electron 게임에서 설정 UI는 전체화면을 켰다고 표시하지만, 실제 창이 1920×1080 크기의 borderless 창으로 좌측 상단에 남는 경우가 있습니다. 이 문제는 HTML Fullscreen API보다 BrowserWindow의 상태와 창 설정 호출 순서를 먼저 확인해야 합니다.

이 글은 Windows의 2560×1440 화면에서 실제로 확인한 진단 흐름을 정리한 것입니다. 핵심은 추측으로 옵션을 바꾸지 않고, 요청 전후의 창 ID·bounds·전체화면 상태·모니터 배율을 함께 로그로 남기는 것입니다.

먼저 확인할 호출 목록

프로젝트 전체에서 아래 호출을 검색해 호출 위치와 실행 순서를 적어 두세요.

  • setFullScreen, setSize, setContentSize, setBounds, setContentBounds
  • maximize, unmaximize, restore, center, setPosition
  • fullscreen, fullscreenable, resizable
  • devicePixelRatio, scaleFactor, screenToDipRect, dipToScreenRect

렌더러의 THREE.WebGLRenderer.setSize()는 캔버스 해상도 조정용입니다. BrowserWindow의 setSize()와 이름은 비슷하지만, 서로 다른 계층의 호출입니다. 문제의 창 크기를 바꾸는 것은 메인 프로세스의 BrowserWindow API인지 먼저 구분하세요.

창 설정 적용을 한 곳으로 모으기

전체화면 직후에 setSize()나 center()가 실행되면 전체화면 상태를 덮어쓸 수 있습니다. 해상도 변경, 설정 로드, IPC 요청, 전체화면 해제에서 창 설정을 각각 적용하지 말고 메인 프로세스 함수 하나로 모으세요.

function applyDisplaySettings(win, settings) {
  const { width, height } = WINDOW_PRESETS[settings.resolution];

  if (settings.fullscreen) {
    // Windows에서 fixed-size 창은 이 준비 단계가 필요할 수 있습니다.
    win.setResizable(true);
    win.setFullScreen(true);
    return win.isFullScreen();
  }

  win.setFullScreen(false);
  win.setSize(width, height);
  win.center();
  win.setResizable(false);
  return win.isFullScreen();
}

전체화면 분기에는 창 크기나 위치를 바꾸는 호출을 넣지 않는 것이 중요합니다. 창 모드로 돌아갈 때만 선택한 프리셋 크기와 중앙 정렬을 적용하세요.

resizable: false가 전체화면을 막는지 비교하기

고정 크기 창은 게임 UI 비율을 지키는 데 편리하지만, Windows 환경에서는 전체화면 전환을 방해할 수 있습니다. 실제 진단에서 resizable: false 상태로 setFullScreen(true)를 호출했을 때는 isFullScreen()이 true였지만 창 bounds가 1920×1080에 머물렀습니다.

같은 창에서 setResizable(true)를 먼저 호출한 뒤 전체화면으로 전환하자 bounds가 2560×1440으로 확장됐습니다. 따라서 창 모드에서만 다시 setResizable(false)로 잠그면, 고정 프리셋 정책과 전체화면을 함께 유지할 수 있습니다.

fullscreenable도 활성화해야 합니다. 다만 Windows의 실제 전환을 검증할 때는 fullscreenable: true만 확인하지 말고, 반드시 전환 후 bounds까지 확인하세요. Electron BrowserWindow 문서에서 setFullScreen(), setResizable(), getBounds()의 플랫폼별 동작을 확인할 수 있습니다.

요청 전후 상태를 모두 로그로 남기기

screen.getDisplayMatching()으로 대상 모니터와 Windows 배율을 함께 기록하면 DPI 이중 변환을 빠르게 제외할 수 있습니다.

const { screen } = require('electron');

function logWindowState(label, win, requestedFullscreen) {
  const bounds = win.getBounds();
  const display = screen.getDisplayMatching(bounds);

  console.log('[Window]', label, {
    requestedFullscreen,
    windowId: win.id,
    isFullScreen: win.isFullScreen(),
    isResizable: win.isResizable(),
    bounds,
    display: {
      bounds: display.bounds,
      workArea: display.workArea,
      scaleFactor: display.scaleFactor,
    },
  });
}

다음 값이 한 로그 묶음에 있어야 원인을 비교할 수 있습니다.

  • 전체화면 요청값과 대상 BrowserWindow ID
  • 요청 전후 isFullScreen() 및 getBounds()
  • 모니터 bounds, work area, scaleFactor
  • 전체화면 요청 뒤 실행된 setSize, setBounds, center 호출 위치

윈도우 크기를 scaleFactor나 렌더러 devicePixelRatio로 나누거나 곱하는 코드는 별도로 확인하세요. BrowserWindow의 DIP 크기와 WebGL의 픽셀 비율을 한 계산에 섞으면 창이 예상보다 작거나 크게 보일 수 있습니다.

이벤트의 즉시 상태를 그대로 UI에 보내지 않기

Windows에서는 enter-full-screen 이벤트가 발생한 직후 isFullScreen()이 아직 이전 값을 반환할 수 있습니다. 이벤트 안에서 즉시 렌더러에 설정을 보내면 UI가 “전체화면 꺼짐”으로 되돌아갈 수 있습니다.

이벤트는 기록용으로 먼저 남기고, 짧은 지연 뒤 실제 상태를 다시 읽어 UI에 전달하세요.

win.on('enter-full-screen', () => {
  logWindowState('enter-full-screen', win, true);
  setTimeout(() => {
    const fullscreen = win.isFullScreen();
    webContents.send('window:settings-changed', { fullscreen });
  }, 100);
});

IPC 응답도 저장하려던 값이 아니라 win.isFullScreen()으로 확인한 결과를 반환해야 합니다. 렌더러는 이 응답과 상태 변경 이벤트를 기준으로 토글 UI를 갱신하세요.

UI·IPC를 건너뛴 진단 모드

전체화면 토글을 누르지 않아도 원인을 분리할 수 있도록, 개발 전용 인자를 두는 방법이 유용합니다.

if (process.argv.includes('--fullscreen-diagnostic')) {
  setTimeout(() => {
    logWindowState('before direct fullscreen', mainWindow, true);
    mainWindow.setResizable(true);
    mainWindow.setFullScreen(true);
    logWindowState('after direct fullscreen', mainWindow, true);
  }, 1000);
}

이 진단에서도 창이 작게 남으면 렌더러·preload·IPC 문제가 아닙니다. 반대로 직접 요청은 정상인데 UI 토글만 실패한다면 preload 경로, IPC 채널 이름, 설정을 다시 덮어쓰는 렌더러 코드를 확인하세요.

배포 전 체크리스트

  • 전체화면과 창 모드 적용이 하나의 메인 프로세스 함수만 거치는가?
  • 전체화면 분기 뒤에 setSize, setBounds, center가 실행되지 않는가?
  • 고정 크기 창이라면 전체화면 진입 전에 resizable 제약을 해제하는가?
  • 전체화면 해제 후에만 프리셋 크기와 중앙 정렬을 적용하는가?
  • UI가 저장값이 아니라 실제 win.isFullScreen() 결과를 표시하는가?
  • 100%, 125%, 150% Windows 배율과 다중 모니터에서 bounds·scaleFactor 로그를 확인했는가?
  • 패키징된 Electron 빌드에서도 진단 로그와 전체화면 전환을 확인했는가?

한 번의 API 호출이 성공처럼 보여도 실제 bounds가 모니터와 같지 않으면 전체화면 문제는 해결된 것이 아닙니다. 상태, 창 경계, 모니터 정보를 같은 로그에서 비교하는 습관이 재현하기 어려운 데스크톱 문제를 가장 빠르게 좁혀 줍니다.