⚡ Electron3. IPC & Bridge📖 개요

03-ipc-bridge — IPC & contextBridge

이 챕터가 답하는 질문: Renderer는 OS에 어떻게 요청을 보내고, 왜 그 사이에 반드시 preload + contextBridge + invoke/handle이 끼어야 하는가? 한 줄 답 (Pyramid Top): “Renderer는 OS에 직접 못 닿는다 — preload에서 contextBridge.exposeInMainWorld좁은 함수만 노출하고, renderer는 그 함수로 ipcRenderer.invoke를 호출하며, main은 ipcMain.handle로 응답한다. 이것이 유일한 안전한 패턴이다.”


한 문장 답 (Pyramid Top)

Electron의 심장은 IPC다. 프로세스를 나눠 권한을 격리한 대가가 모든 OS 접근이 IPC를 거쳐야 한다는 것이고, 그 IPC를 얼마나 좁게 설계하느냐가 곧 얼마나 안전한 앱이냐다. 웹의 postMessage와 비슷해 보이지만 — postMessage같은 origin의 다른 frame에 메시지를 보내는 것이고, Electron IPC는 권한 등급이 다른 프로세스 사이를 잇는 권한 게이트다. 그래서 별도의 모듈(ipcMain/ipcRenderer), 별도의 격리(contextIsolation), 별도의 노출 API(contextBridge)가 모두 필요했다. 이 챕터는 왜 IPC인가부터 시작해서, 두 모듈의 정확한 API, contextBridge의 역할, invoke/handle 패턴, MessagePort 양방향 채널, 흔한 안티패턴과 CVE 사례까지를 한 층씩 벗긴다.


IPC 흐름 (Mermaid)


챕터 지도


Why — 왜 이 챕터가 별도인가 (도메인의 심장)

Electron 도메인에서 모든 사고의 진앙이 IPC다. 프로세스를 나눈 이유(=권한 격리)가 바로 그 IPC 통로 하나로 무효화될 수 있기 때문이다.

잘못된 직관실제어디서 다루나
”IPC는 그냥 함수 호출의 비동기 버전”권한 등급이 다른 프로세스 사이의 통로 — 함수 호출이 아니라 권한 위임이다.01
ipcRenderer.send 하나면 다 된다”send/on은 fire-and-forget — 응답 받으려면 invoke/handle. sendSyncUI freeze.02
”preload에 Node 풀어주면 편하다”XSS 한 번에 OS 명령 실행. contextBridge좁게 노출하는 게 유일한 안전.03
”타입 없이 채널 이름으로 통신해도 충분”채널 이름 충돌·시그니처 어긋남이 런타임에야 터진다. TS declaration 공유가 표준.04
”대용량 전송도 invoke로”Buffer 직렬화 비용·메모리 복사가 폭발. *MessagePortMain*과 transferable이 답.05
”안티패턴은 알아서 피하겠지”CVE 데이터베이스가 반증이다. 매년 같은 실수가 RCE로 발견된다.06

이 챕터는 어떻게함정의 세 층으로 짜여 있다.


본문 6개 인덱스

#파일핵심 키워드분량
0101-why-ipc.md프로세스 격리 · postMessage 비교 · 권한 게이트8-10KB
0202-ipcmain-ipcrenderer.mdsend · on · invoke · handle · sendSync 금기 · 채널 컨벤션10-12KB
0303-contextbridge.mdexposeInMainWorld · nodeIntegration: true 폐기 이유 · API 표면 설계10-14KB
0404-invoke-handle-pattern.mdPromise 기반 · 에러 직렬화 · declare global { Window } · 타입 공유10-12KB
0505-messageport-and-streaming.mdMessagePortMain · transferable · Utility Process · 양방향 채널8-10KB
0606-ipc-antipatterns.md채널 충돌 · 과한 노출 · sendSync freeze · remote 제거 · CVE 사례8-12KB

What-if — IPC를 잘못 설계하면 = RCE

IPC는 *권한이 낮은 프로세스(Renderer)*가 *권한이 높은 프로세스(Main)*에게 대신 일해달라고 부탁하는 메커니즘이다. 부탁의 통로(=채널)를 넓게 만들면 — 그 부탁이 공격으로 변질되는 순간 막을 길이 없다.

  • ipcMain.handle('exec', cmd => exec(cmd)) 한 줄이면 — XSS 페이로드 한 번이 OS 명령 실행으로 직결된다.
  • contextBridge 없이 nodeIntegration: true: renderer에서 require('child_process')가 그대로 호출 가능. 2018년 이전 Electron 앱들이 이 패턴으로 줄줄이 CVE를 맞았다 (CVE-2018-1000136, CVE-2018-15685 등).
  • remote 모듈 (v14에서 제거): 메인 프로세스의 임의 객체를 renderer에서 그대로 호출하게 했다 — 너무 위험해서 언어 차원에서 제거했다.
  • 채널 이름 충돌: 'data' 같은 일반명을 채널로 쓰면 라이브러리끼리 충돌하고, 한 채널이 다른 채널의 응답을 가로채는 일이 벌어진다.

이 챕터를 건너뛰면 — 어느 한 줄이 RCE인지 감별할 능력을 못 얻는다.


Mermaid 4색 규약


한 단락 요약

Renderer는 권한이 낮다. OS에 닿으려면 권한이 높은 Main에게 부탁해야 하고, 그 부탁의 통로가 IPC다. 그 통로는 좁아야 안전하다 — preload에서 contextBridge함수 단위로 노출하고(03), renderer는 그 함수로 invoke(02, 04)를, main은 handle로 받는다. 대용량·양방향은 MessagePort로(05). 흔한 함정은 넓은 노출, sync 통신, 채널 이름 충돌, 원격 객체 위임이고 — 이 함정들은 매년 CVE가 새로 나온다(06). 이 챕터를 끝내면 “이 IPC 채널이 안전한가” 라는 질문에 코드 한 줄로 답할 수 있게 된다. 다음 챕터(04-native-integration)는 그 통로 너머의 실제 OS API가 어떻게 생겼는지를 본다.