"webContents는 BrowserWindow 안의 웹 페이지를 제어하는 리모컨" — 페이지 로드, JavaScript 실행, 인쇄, 검색 등 렌더러에서 할 수 있는 거의 모든 것을 메인에서 제어할 수 있습니다.


webContents 접근하기

JAVASCRIPT
const { BrowserWindow } = require('electron');

const win = new BrowserWindow({ width: 800, height: 600 });

// BrowserWindow에서 webContents 접근
const contents = win.webContents;

// 모든 webContents 가져오기
const { webContents } = require('electron');
const allContents = webContents.getAllWebContents();

페이지 로딩

JAVASCRIPT
// 로컬 파일 로드
win.webContents.loadFile('index.html');

// URL 로드
win.webContents.loadURL('https://example.com');

// 로드 완료 이벤트
win.webContents.on('did-finish-load', () => {
  console.log('페이지 로드 완료');
});

// 로드 실패 이벤트
win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
  console.error(`로드 실패: ${errorCode} - ${errorDescription}`);
  // 오프라인 페이지 표시
  win.webContents.loadFile('offline.html');
});

// DOM 준비 이벤트 (로드 완료 전에 발생)
win.webContents.on('dom-ready', () => {
  console.log('DOM이 준비되었습니다');
});

JavaScript 실행

JAVASCRIPT
// 렌더러에서 JavaScript 실행
const result = await win.webContents.executeJavaScript(`
  document.title
`);
console.log('페이지 타이틀:', result);

// 복잡한 스크립트 실행
await win.webContents.executeJavaScript(`
  (function() {
    const elements = document.querySelectorAll('.item');
    return Array.from(elements).map(el => ({
      text: el.textContent,
      href: el.href
    }));
  })()
`);

주의: executeJavaScript는 보안상 주의가 필요합니다. 사용자 입력을 직접 실행하지 마세요.


네비게이션 제어

JAVASCRIPT
// 네비게이션 이벤트 감지
win.webContents.on('will-navigate', (event, url) => {
  // 외부 URL로의 이동을 차단하고 브라우저에서 열기
  if (!url.startsWith('file://')) {
    event.preventDefault();
    const { shell } = require('electron');
    shell.openExternal(url);
  }
});

// 새 창 열기 요청 처리
win.webContents.setWindowOpenHandler(({ url }) => {
  // 외부 링크는 기본 브라우저에서 열기
  if (url.startsWith('https://')) {
    shell.openExternal(url);
    return { action: 'deny' };
  }
  // 내부 링크는 새 Electron 창에서 열기
  return { action: 'allow' };
});

// 뒤로/앞으로 네비게이션
win.webContents.goBack();
win.webContents.goForward();
win.webContents.canGoBack();    // boolean
win.webContents.canGoForward();  // boolean

페이지 내 검색 (Find in Page)

JAVASCRIPT
// main.js
ipcMain.on('find:start', (event, text) => {
  const contents = BrowserWindow.fromWebContents(event.sender).webContents;
  contents.findInPage(text);
});

ipcMain.on('find:next', (event, text) => {
  const contents = BrowserWindow.fromWebContents(event.sender).webContents;
  contents.findInPage(text, { forward: true, findNext: true });
});

ipcMain.on('find:stop', (event) => {
  const contents = BrowserWindow.fromWebContents(event.sender).webContents;
  contents.stopFindInPage('clearSelection');
});

// 검색 결과 이벤트
win.webContents.on('found-in-page', (event, result) => {
  console.log(`검색 결과: ${result.activeMatchOrdinal}/${result.matches}`);
  win.webContents.send('find:result', {
    current: result.activeMatchOrdinal,
    total: result.matches,
  });
});

인쇄

JAVASCRIPT
// 기본 인쇄 다이얼로그
win.webContents.print();

// 인쇄 옵션 지정
win.webContents.print({
  silent: false,           // false면 인쇄 다이얼로그 표시
  printBackground: true,   // 배경색/이미지 인쇄
  margins: {
    marginType: 'custom',
    top: 1,
    bottom: 1,
    left: 1,
    right: 1,
  },
});

// PDF로 저장
const pdfData = await win.webContents.printToPDF({
  printBackground: true,
  pageSize: 'A4',
  margins: { top: 0, bottom: 0, left: 0, right: 0 },
});

await fs.promises.writeFile('output.pdf', pdfData);

줌 제어

JAVASCRIPT
// 줌 레벨 (0이 기본, 양수가 확대, 음수가 축소)
win.webContents.setZoomLevel(0);     // 100%
win.webContents.setZoomLevel(1);     // ~120%
win.webContents.setZoomLevel(-1);    // ~80%

// 줌 팩터 (1.0이 100%)
win.webContents.setZoomFactor(1.5);  // 150%

// 현재 줌 레벨 가져오기
const level = win.webContents.getZoomLevel();
const factor = win.webContents.getZoomFactor();

DevTools 제어

JAVASCRIPT
// DevTools 열기/닫기
win.webContents.openDevTools();
win.webContents.openDevTools({ mode: 'detach' }); // 별도 창
win.webContents.closeDevTools();
win.webContents.toggleDevTools();

// DevTools 열려있는지 확인
const isOpen = win.webContents.isDevToolsOpened();

유용한 이벤트들

JAVASCRIPT
// 콘솔 메시지 가로채기
win.webContents.on('console-message', (_event, level, message) => {
  const levels = ['verbose', 'info', 'warning', 'error'];
  console.log(`[Renderer ${levels[level]}] ${message}`);
});

// 크래시 감지
win.webContents.on('render-process-gone', (_event, details) => {
  console.error('렌더러 크래시:', details.reason);
  // 렌더러 재시작
  win.webContents.reload();
});

// 응답 없음 감지
win.webContents.on('unresponsive', () => {
  dialog.showMessageBox(win, {
    type: 'warning',
    title: '응답 없음',
    message: '앱이 응답하지 않습니다. 기다리시겠습니까?',
    buttons: ['기다리기', '강제 종료'],
  }).then(({ response }) => {
    if (response === 1) win.destroy();
  });
});

win.webContents.on('responsive', () => {
  console.log('렌더러가 다시 응답합니다');
});

핵심 포인트 정리

  • webContents는 BrowserWindow 내부의 웹 페이지를 제어하는 핵심 API
  • setWindowOpenHandler로 새 창 열기 동작을 커스터마이징
  • will-navigate로 외부 URL 이동을 차단하고 기본 브라우저로 리다이렉트
  • render-process-gone 이벤트로 렌더러 크래시를 감지하고 복구
  • executeJavaScript는 강력하지만 보안에 주의 필요

webContents를 다뤘으면, 다음은 세션과 쿠키 관리를 알아봅시다.

댓글 로딩 중...