⚡ Electron4. 네이티브 통합📖 개요

04 — Native Integration (챕터 인덱스)

이 챕터가 답하는 질문: “Electron이 진짜 데스크톱 앱이 되려면 OS에 닿아야 한다 — 그 닿음을 어디서·누가·어떻게 하느냐?” 한 줄 답 (Pyramid Top): “Node API와 OS API에는 Main 프로세스만 닿고, 렌더러는 IPC를 통해 결과만 받는다 — Renderer가 OS에 직접 닿는 순간 Electron의 보안 모델은 전부 무너진다.”


챕터 지도


Why — 왜 네이티브 통합 챕터가 따로 존재하는가

웹 개발자가 가장 헷갈리는 지점이 여기다. 브라우저에서 fs.readFile을 부를 수 없는 이유권한이 없어서가 아니라 권한 모델 자체가 다르기 때문이다.

환경OS 접근 방식권한 부여 시점권한 범위
브라우저Web API (sandbox 안)사용자 prompt도메인 + 1회성
Electron Renderer직접 못 닿음 (default)0
Electron MainNode fs/child_process 전부앱이 시작되는 순간OS 사용자 전체 권한

Renderer가 XSS에 한 번 뚫리면 사용자 홈 디렉토리 전체가 노출된다 — ~/.ssh/id_rsa까지. 그래서 모든 OS 접근은 Main만이 한다 가 디폴트 룰이다.

이 챕터는 그 룰을 어떻게 지키면서 동시에 데스크톱 앱답게 만드는가의 7가지 패턴을 다룬다.


본문 7개 인덱스

#파일다루는 주제핵심 키워드
01fs · pathNode fs/path로 사용자 데이터 읽고 쓰기. app.getPath('userData')로 OS별 경로 해결. 웹의 File System Access API와 권한 모델 비교fs · path · app.getPath · userData · File System Access API · notarization entitlement
02shell · OS APIshell.openExternal/openPath, clipboard, nativeImage, powerMonitor. URL 열기의 보안 함정 (사용자 입력 직접 넘기지 말기)shell · clipboard · nativeImage · powerMonitor · openExternal RCE
03Menu · TrayMenu/MenuItem/Tray. macOS 메뉴바 vs Windows 시스템 트레이의 철학 차이. 컨텍스트 메뉴Menu · MenuItem · Tray · accelerator · role · macOS menu bar
04Notification · dialogElectron Notification(웹 표준의 확장), dialog.showOpenDialog/showMessageBox. 비동기 vs 동기 함정Notification · dialog.showOpenDialog · showMessageBox · sheet · modal
05Native Modules · N-APIC++ 모듈을 Electron ABI에 맞춰 빌드. electron-rebuild · prebuild. better-sqlite3 · sharp · keytarnode-gyp · N-API · node-addon-api · electron-rebuild · prebuild · ABI 불일치
06Custom Protocolprotocol.registerFileProtocol/registerStreamProtocol로 로컬 자원을 안전하게 노출. file:// 직접 노출 금지protocol.handle · registerFileProtocol · app:// · file:// 금지 · CSP
07CLI · child_processchild_process.spawn으로 외부 CLI(ffmpeg·git) 호출. 쉘 인젝션 방지. macOS/Windows 경로·실행 권한 차이spawn · execFile · shell=true 금지 · argv array · PATH · cmd.exe vs bash

어떻게 읽는가

  • Electron 첫 OS 통합: 01 → 04 → 03. 파일 읽고 / 다이얼로그로 고르고 / 메뉴로 노출하면 1차 데스크톱 경험 완성.
  • 무거운 데이터 다루는 앱(SQLite/이미지): 05 → 06 → 01. 네이티브 모듈 ABI 정착 → 로컬 자원 노출 채널 정리 → 파일 입출력.
  • 트레이 상주 앱 / 시스템 유틸리티: 03 → 02 → 04. 메뉴바 상주 → OS 이벤트 구독 → 알림으로 사용자 호출.
  • 외부 도구 wrapper(영상 변환 · git GUI): 07 → 01 → 04. 외부 CLI 안전 호출 → 결과 파일 처리 → 진행 다이얼로그.

What — 챕터를 관통하는 7가지 사실

#사실어디서 다루나
1사용자 데이터는 app.getPath('userData') 하나로 다 해결된다 — 절대 직접 ~/.config/MyApp 같은 식으로 작성하지 말 것01-fs-and-path
2macOS notarization 후에는 ~/Documents·~/Desktop별도 entitlement 없으면 못 읽는다01-fs-and-path
3shell.openExternal(userInput)RCE 통로다javascript: 스킴 한 줄로 끝남02-shell-and-os
4macOS 메뉴바와 Windows 트레이는 겉모습은 비슷해도 OS 모델이 다르다 — 같은 코드로 두 경험을 못 만든다03-menus-and-tray
5네이티브 모듈은 Node ABI가 아니라 Electron ABI에 맞춰야 한다 — electron-rebuild가 그 일을 한다05-native-modules-and-n-api
6file://를 그냥 렌더러에 노출하면 사용자 홈 전체가 fetch 한 줄로 읽힌다 — custom scheme + protocol handler가 정답06-protocol-and-custom-scheme
7child_process.exec(userInput)쉘 인젝션spawn(cmd, [args], {shell:false}) + argv 배열이 표준07-cli-and-child-process

What-if — 권한이 너무 커서 위험하다

OS 권한이 디폴트로 전부 켜져 있다는 사실이 이 챕터의 모든 함정의 출처다.

함정증상원인 챕터
app.getPath 안 쓰고 hardcoded pathWindows에서 ~//c/Users/raw/가 아니라 풀려서 ENOENT01
notarization 통과 후 ~/Documents 안 열림read access denied — entitlement 누락01
shell.openExternal(req.body.url) 그대로 호출javascript:require('child_process').exec(...) 한 줄로 OS 명령 실행02
Windows에 macOS Tray 코드 그대로트레이는 뜨지만 메뉴바 통합은 안 됨03
Notification을 Renderer에서만 띄움권한 prompt가 Chrome 스타일로 떠서 사용자 혼란04
Electron 메이저 업그레이드 후 import 'better-sqlite3'NODE_MODULE_VERSION 119 vs 125 에러 — ABI 불일치05
<img src="file:///etc/passwd">가 떠 버림webSecurity:false 또는 file:// 노출06
spawn('git', ['log', branchName])branchName = "; rm -rf /"shell=false면 안전, shell=true면 재앙07

Insight — 한 단락 이야기

“VS Code의 파일 트리가 빠른 이유는 fs를 안 쓰기 때문이다”

VS Code 팀은 초기에 모든 fs.readdir을 Renderer에서 IPC로 부르다 수만 개 파일이 있는 모노레포에서 freeze가 발생했다. 해법은 두 가지였다 — (a) fs 호출을 Main에서 Utility Process로 분리해 메인 이벤트 루프를 비워두기, (b) native module(node-watch / chokidar의 native backend)로 OS 레벨 inotify·FSEvents를 직접 구독하기. 즉 “Renderer ↔ Main의 IPC만 잘 짜면 된다”는 1차 답이 아니라 Main도 너무 바쁘면 안 된다 → Utility Process로 한 번 더 분리 → 거기서 네이티브 모듈을 부른다는 2차 답이 정답이었다. 이 챕터의 진짜 메시지가 이거다 — “Main이 OS 창구”라는 룰을 지키되, Main 안에서도 작업을 잘게 쪼개라.


한 단락 요약

네이티브 통합 챕터는 Renderer가 OS에 직접 못 닿는다는 룰 위에서 Main이 어떻게 OS의 일곱 면(파일·쉘·메뉴·알림·네이티브 모듈·로컬 자원·외부 CLI)에 안전하게 닿는가를 일곱 문서로 펼친다. 각 문서가 공통으로 던지는 질문은 “이 호출이 사용자 입력을 직접 받는가? 받는다면 어떻게 검증하는가?” — 권한이 너무 커서 검증 없는 호출이 곧 RCE다. 이 챕터를 끝내면 “OS에 어떻게 닿느냐”가 아니라 *“OS에 닿기 전에 무엇을 검증했느냐”*를 먼저 묻게 된다.