webContents — 렌더러 제어의 핵심 API
"webContents는 BrowserWindow 안의 웹 페이지를 제어하는 리모컨" — 페이지 로드, JavaScript 실행, 인쇄, 검색 등 렌더러에서 할 수 있는 거의 모든 것을 메인에서 제어할 수 있습니다.
webContents 접근하기
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();
페이지 로딩
// 로컬 파일 로드
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 실행
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는 보안상 주의가 필요합니다. 사용자 입력을 직접 실행하지 마세요.
네비게이션 제어
// 네비게이션 이벤트 감지
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)
// 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,
});
});
인쇄
// 기본 인쇄 다이얼로그
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);
줌 제어
// 줌 레벨 (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 제어
// DevTools 열기/닫기
win.webContents.openDevTools();
win.webContents.openDevTools({ mode: 'detach' }); // 별도 창
win.webContents.closeDevTools();
win.webContents.toggleDevTools();
// DevTools 열려있는지 확인
const isOpen = win.webContents.isDevToolsOpened();
유용한 이벤트들
// 콘솔 메시지 가로채기
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 내부의 웹 페이지를 제어하는 핵심 APIsetWindowOpenHandler로 새 창 열기 동작을 커스터마이징will-navigate로 외부 URL 이동을 차단하고 기본 브라우저로 리다이렉트render-process-gone이벤트로 렌더러 크래시를 감지하고 복구executeJavaScript는 강력하지만 보안에 주의 필요
webContents를 다뤘으면, 다음은 세션과 쿠키 관리를 알아봅시다.