[Electron] Preload 스크립트

2025. 4. 28. 23:28·JavaScript/Electron

 

Electron을 사용하면서 preload.js를 제대로 이해하고 써야만
보안 강화, 안정성 향상, 성능 최적화를 모두 챙길 수 있습니다.

하지만 많은 초심자들이 preload를

  • "그냥 있나보다"
  • "대충 쓰면 되지"
    정도로 넘깁니다.

그러면 앱이 위험해지고, 유지보수가 어려워지고, 퍼포먼스가 떨어집니다.

오늘 이 글에서
preload의 역할, 작성법, 보안 설정, 고급 패턴까지
처음부터 끝까지 꼼꼼하게 정리해봅시다.

 

Preload란 무엇인가?

Preload 스크립트는 렌더러 프로세스가 시작되기 전에 실행되는
Node.js 환경의 JavaScript 코드입니다.

쉽게 말하면?

  • 메인 프로세스가 브라우저 창을 열 때
  • 웹 페이지(렌더러)가 로드되기 전에
  • 미리 필요한 기능을 준비하는 중간 다리 역할입니다.

Electron 공식 정의

Preload scripts run in an isolated context before the renderer process is loaded.

Preload를 왜 써야 할까?

Node.js 기능을 UI에 연결하기 위해

렌더러는 원래 Node.js 기능에 직접 접근하지 못합니다. (특히 contextIsolation: true 설정 시)

그래서 preload를 통해 Node.js API (fs, os, ipcRenderer, ...)를 안전하게 노출시켜야 합니다.

 

보안 강화

Electron의 가장 위험한 지점은
렌더러 프로세스 = 외부 HTML/JS
을 열 수 있다는 점입니다.

렌더러가 Node.js 전체를 쥐고 있으면?

  • XSS 한 방에 시스템 파일 삭제 가능
  • 백도어 삽입 위험

Preload + contextBridge를 사용하면,
렌더러에 최소한의 기능만 노출할 수 있습니다.

성능 최적화

필요한 라이브러리만 preload에 적재하면
초기 로딩 속도를 높이고, 불필요한 메모리 사용을 줄일 수 있습니다.

기본 Preload 작성하기

Preload 파일 생성

프로젝트에 preload.js 파일을 만듭니다.

// preload.js

const { contextBridge, ipcRenderer } = require('electron');

// expose API to renderer
contextBridge.exposeInMainWorld('electronAPI', {
  sendLog: (message) => ipcRenderer.send('log-message', message),
  fetchData: (key) => ipcRenderer.invoke('get-data', key)
});

BrowserWindow에 preload 연결

메인 프로세스에서 BrowserWindow를 만들 때 preload를 지정합니다.

// main.js

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

const win = new BrowserWindow({
  width: 800,
  height: 600,
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,
    nodeIntegration: false,
    enableRemoteModule: false
  }
});

주의:
contextIsolation: true, nodeIntegration: false는 반드시 설정해야 안전합니다.

Preload 스크립트와 contextBridge

contextBridge는 렌더러 프로세스에
선별된 안전한 API만 노출하는 모듈입니다.

기본 구조

contextBridge.exposeInMainWorld('key', {
  methodName: () => { /* logic */ }
});
  • key: 렌더러에서 접근할 때 사용할 전역 객체 이름
  • methodName: 렌더러가 호출 가능한 함수

렌더러에서는 이렇게 사용

// renderer.js
electronAPI.sendLog('User clicked!');
  • Node.js, ipcRenderer 같은 내부 모듈은 직접 접근 불가
  • 오직 electronAPI를 통해서만 접근 가능

보안 관점에서 본 preload

반드시 지켜야 할 설정

옵션 설명
contextIsolation: true 렌더러를 분리된 context에서 실행
nodeIntegration: false 렌더러에서 Node.js 기능 차단
enableRemoteModule: false remote 모듈 비활성화

 

(※ Electron 12 이상은 기본적으로 contextIsolation: true입니다.)

위험한 패턴

아래처럼 preload에서 전역 오염시키면 절대 안됩니다.

// preload.js (BAD)
window.ipcRenderer = require('electron').ipcRenderer;
  • 이렇게 하면 렌더러가 ipcRenderer를 직접 조작할 수 있습니다.
  • 공격자가 렌더러 스크립트를 탈취하면 시스템 접근 가능.

활용: 다중 preload 관리

프로젝트가 커지면 여러 기능을 preload에 나눌 수 있습니다.

구조 예시

/preload
    /modules
        - ipc.js
        - fileSystem.js
        - auth.js
    preload.js

preload.js

const { contextBridge } = require('electron');
const ipc = require('./modules/ipc');
const fileSystem = require('./modules/fileSystem');
const auth = require('./modules/auth');

contextBridge.exposeInMainWorld('electronAPI', {
  ...ipc,
  ...fileSystem,
  ...auth
});

모듈별로 관리하면 유지보수성, 확장성이 뛰어나집니다.

실전 예제: 파일 읽기 기능 추가

preload.js

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  readFile: (path) => ipcRenderer.invoke('read-file', path)
});

main.js

const fs = require('fs').promises;

ipcMain.handle('read-file', async (event, path) => {
  return await fs.readFile(path, 'utf8');
});

renderer.js

async function loadFile(path) {
  const content = await window.electronAPI.readFile(path);
  console.log(content);
}

정리

  • Preload는 렌더러가 시작되기 전 Node.js 코드 실행용
  • contextBridge로 안전하게 필요한 기능만 노출
  • nodeIntegration: false, contextIsolation: true는 필수
  • 보안, 유지보수, 성능을 모두 고려해야 함
  • 모듈화하여 구조화하는 습관을 들이자
728x90
'JavaScript/Electron' 카테고리의 다른 글
  • [Electron] IPC 통신
츄핑
츄핑
    250x250
  • 츄핑
    개발로그
    츄핑
  • 전체
    오늘
    어제
    • 분류 전체보기
      • CS
        • 자료구조
        • 알고리즘
        • 운영체제
        • 네트워크
        • 데이터베이스
        • 인프라
        • Web
      • PS
        • 백준
      • JavaScript
        • React
        • Express
        • NestJS
        • TypeScript
        • Node.js
        • Electron
      • Java
        • Spring
      • Dart
        • Flutter
      • PHP
        • CodeIgniter
      • etc
        • 이산수학
        • 선형대수학
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    오블완
    티스토리챌린지
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
츄핑
[Electron] Preload 스크립트
상단으로

티스토리툴바