⚡ Electron1. 프로세스 모델01 — 왜 멀티프로세스인가

01 — 왜 멀티프로세스인가

한 줄 답: Electron이 멀티프로세스인 건 선택이 아니라 상속이다 — Chromium이 이미 그런 모델이고, Electron은 그 위에 데스크톱 OS 권한 경계를 얹었다. 그래서 웹 브라우저는 개발자가 모르게 멀티프로세스지만, Electron은 개발자가 직접 설계해야 하는 멀티프로세스다.


Why — 왜 단일 프로세스로 못 만드는가

“브라우저 한 개 + Node 한 개를 한 프로세스에 합치면 안 되나?” — 충분히 합리적인 질문이다. 실제로 2008년 이전의 브라우저는 그랬다. Firefox 3.x, IE 6/7, Safari 초기는 모두 단일 프로세스였다.

그게 망가진 이유 셋.

1) 한 탭이 죽으면 전부 죽었다

Flash 한 개가 segfault → 브라우저 전체 종료. 사용자가 30분 동안 쓰던 다른 탭 19개도 같이 사라진다. 2008년 “sad tab” 아이콘이 등장하기 전, 사용자는 “브라우저 멈춤” 만을 봤다.

2) 한 탭이 메모리를 먹으면 전부 느려졌다

GC가 한 V8 힙을 정리할 때 다른 탭들도 같이 멈췄다. V8은 격리(isolate) 단위로 GC를 돌리지만, 한 프로세스 안의 모든 isolate는 같은 OS 스레드에서 스케줄된다.

3) 한 탭이 뚫리면 시스템이 뚫렸다

Flash 0-day → 브라우저 프로세스 권한 = 사용자 권한 → 디스크 전체 읽기. sandbox가 없으면 XSS의 영향이 origin에 머무르지 않는다.

Chrome이 2008년 “Multi-process architecture” 선언과 함께 등장한 이유다. 이게 The Chromium Projects: Multi-process Architecture 문서의 시작점.


How — Chromium의 사이트 격리를 Electron이 어떻게 물려받았나

Chromium의 프로세스 모델은 4계층이다.

각 Renderer는 별도 OS 프로세스별도 sandbox에 갇혀 있다. Renderer가 OS API를 부르고 싶으면 Browser Process에게 IPC로 요청해야 한다.

Electron은 이 구조를 거의 그대로 가져왔다. 단어만 바뀐다.

ChromiumElectron책임
Browser ProcessMain Process앱 라이프사이클, 창 생성, OS API, 추가로 Node.js 전권
Renderer ProcessRenderer Process웹페이지(HTML/CSS/JS) 렌더링, V8 격리
(없음)Preload ScriptRenderer가 시작되기 직전, 같은 프로세스 안의 특권 컨텍스트에서 1회 실행
Utility ProcessUtilityProcess (v22+)Node 워커 분리용
GPU ProcessGPU ProcessGPU 가속 (그대로)

핵심 차이는 두 가지.

  1. Main 프로세스에 Node.js 전권이 박혀 있다. Chrome의 Browser Process는 내부 코드만 Node-like 권한을 가지지만, Electron의 Main에서는 사용자가 작성한 JSfs, child_process, os를 자유롭게 쓴다.
  2. Preload라는 새 개념이 추가됐다. Renderer가 OS에 닿으려면 반드시 Preload를 거치라는 통로다 (04).

What — 한 Electron 앱이 떴을 때 실제로 보이는 프로세스

VS Code 같은 실제 앱을 켜고 ps를 찍어보자.

$ ps aux | grep -i "visual studio code" | awk '{print $11, $12}' | sort -u
.../Electron Framework.framework/Versions/A/Helpers/Electron Helper (GPU).app/...
.../Electron Framework.framework/Versions/A/Helpers/Electron Helper (Plugin).app/...
.../Electron Framework.framework/Versions/A/Helpers/Electron Helper (Renderer).app/...
.../Electron Framework.framework/Versions/A/Helpers/Electron Helper.app/...
.../Visual Studio Code.app/Contents/MacOS/Electron       # ← Main

리스트 한 줄 한 줄이 별도 OS 프로세스다. macOS Activity Monitor에서 “Code” 하나만 띄워도 보통 7~12개 프로세스가 보인다.

Code (Main)                       ← 항상 1개
  ├─ Code Helper (GPU)            ← 항상 1개
  ├─ Code Helper (Plugin)         ← Pepper plugin (거의 안 씀)
  ├─ Code Helper (Renderer) × N   ← 창 N개당 N개
  └─ Code Helper × M              ← Extension Host, Shared Process 등

메모리도 그만큼 곱해진다. 한 Renderer가 80~150MB, GPU 100MB, Main 100MB → 빈 창 하나 띄워도 300MB 이상. 이게 07 — 성능 & 메모리에서 자세히 다룰 Electron의 구조적 비용이다.


본질적 차이 — 브라우저는 몰래, Electron은 드러난

같은 멀티프로세스지만 누가 보느냐가 다르다.

웹 개발자에게 프로세스는 존재하지 않는 개념이다 — window 하나가 세계의 전부다. iframe을 쓰면서도 “다른 프로세스에서 돌고 있다”고 의식하지 않는다.

Electron 개발자는 그 의식을 강제로 한다. 파일 3개로 시작한다는 사실 자체가 — main.js, preload.js, renderer.js (또는 index.html) — 그 의식의 표상이다.

이게 가장 큰 인지 부담이다. Node도 쓸 줄 알고 React도 쓸 줄 아는데 Electron이 어렵게 느껴지는 이유의 90%는 여기서 온다.


프로세스 식별 — process.type

어떤 프로세스에서 코드가 도는지 런타임에 확인할 수 있다.

// 어디서든 동일
console.log(process.type)
// Main:     'browser'   ← 역사적 이름. "browser process"의 약자
// Renderer: 'renderer'
// Preload:  'renderer'  ← 같은 프로세스이므로 'renderer'로 나온다
// Utility:  'utility'

주의: preload는 별도 type을 갖지 않는다. preload는 Renderer 프로세스 안의 isolated world에서 도는 코드이기 때문에 process.type === 'renderer'다. preload인지 메인 월드인지 구분하려면 process.contextIsolated 같은 다른 플래그를 본다.

// preload.js
console.log(process.type)              // 'renderer'
console.log(process.contextIsolated)   // true (디폴트, v12+)
console.log(typeof window.require)      // 'undefined' (sandbox ON일 때)

What-if — 단일 프로세스로 만들면 어떻게 될까

가설로 그려보자.

상황단일 프로세스라면멀티프로세스이면
사용자가 거대한 PDF를 렌더링UI 전체 freeze그 창만 느려짐, 다른 창·메뉴 멀쩡
외부 사이트에서 JS 에러앱 전체 죽음그 Renderer만 죽고 자동 복구 가능
악성 광고 코드가 fs.readFile('/etc/passwd') 시도그대로 읽힘sandbox에 막힘
한 페이지가 메모리 누수앱 전체가 천천히 OOM그 페이지 새로고침으로 해결
Chromium upgrade로 V8 crash앱이 못 뜸Renderer만 crash, Main은 살아 재시작 시도 가능

그리고 반대 비용도 있다. 한 창당 80MB·시작 100ms·IPC 직렬화 오버헤드. Electron이 가진 모든 비용은 이 모델의 그림자다. 비용을 알면 어디서 줄일지도 보인다 (07).


Insight — 사이트 격리는 2017년에 완성된 모델

흥미로운 디테일: Chromium의 Site Isolation은 2017년에야 디폴트가 됐다. 그 전까지는 같은 origin 안의 iframe은 부모와 같은 프로세스에서 돌았다.

그 변화의 트리거가 — Spectre/Meltdown (2018년 1월 공개)이다. CPU의 speculative execution을 이용해 같은 프로세스의 메모리를 읽을 수 있다는 게 드러난 순간, 브라우저는 origin마다 프로세스를 다르게 두지 않으면 안 되게 됐다. 이게 Site Isolation의 완전 활성화 시점이다.

Electron은 그 변화를 자동으로 물려받았다. v6 (2019)부터 Site Isolation이 디폴트로 켜진 Chromium을 번들로 가져왔고, v20 (2022)부터는 sandbox도 디폴트가 됐다. 이게 Electron의 보안 디폴트가 점점 조여지는 방향으로 가는 큰 배경이다.

즉, 멀티프로세스 모델은 “성능·안정성” 문제가 아니라 “근본적 메모리 격리” 문제가 됐다. 이걸 알면 왜 sandbox와 contextIsolation을 끄지 말라는 권고가 그렇게 강한지 이해된다 — 끄는 순간 Spectre급 사이드채널이 다시 열린다.


요약

멀티프로세스는 Chromium에서 시작된 보안·안정성·반응성의 답이고, Electron은 그걸 데스크톱 OS 권한 경계로 확장했다. 핵심 차이는 — 웹 개발자는 모르고 쓰는 멀티프로세스가, Electron에서는 개발자가 직접 설계해야 하는 멀티프로세스가 된다는 것. 그래서 다음 세 문서가 이어진다 — Main(02)·Renderer(03)·Preload(04)의 역할 분담. 이 셋의 분리 이유를 모르면 Electron 코드는 반드시 어디선가 무너진다.