⚡ Electron0. Electron의 기초03 — Web vs Electron vs Native

03 — Web vs Electron vs Native

이 문서가 답하는 질문: “내 앱이 브라우저 안에 있을 때, Electron 안에 있을 때, 네이티브로 짤 때 — 권한·배포·시작비용·OS API·보안 모델이 정확히 어떻게 달라지는가? 그 차이가 내 의사결정에 어떻게 들어와야 하는가?” 한 줄 답 (Pyramid Top): “브라우저 웹은 격리·안전·즉시 갱신을 OS 접근 불가의 대가로 얻는다. 네이티브는 OS 전부에 닿는 대신 플랫폼별 코드를 N번 짠다. Electron은 그 사이에서 같은 코드로 OS에 닿는다 — 격리·배포·보안의 책임을 직접 지는 대가로.”


Why — 왜 3축 비교가 필요한가

회의실에서 자주 듣는 잘못된 프레임 두 가지.

잘못된 프레임실제
”웹앱이냐 데스크톱 앱이냐”이항 비교. 실제로는 웹 / Electron / 네이티브 세 자리가 있고 PWA·Tauri 등 중간 자리도 있다
”Electron은 브라우저에 Node 끼운 것”권한 모델배포 모델완전히 다르다. 브라우저 직관으로 풀 수 없는 문제가 매일 발생

이 문서는 세 자리(웹·Electron·네이티브)를 동일한 6개 축으로 평가한다 — 권한·배포·시작비용·OS API·보안 모델·UI 통합. 축이 같아야 비교가 비교가 된다.

이 문서는 챕터 전체에서 가장 핵심이다 — 다음 챕터들의 모든 디자인 결정이 이 비교의 어떤 칸에서 따라 나오는지 매번 참조된다.


How — 6축 비교 한 장

세 자리가 6개 축에서 각각 어떻게 다른지를 축 단위로 본다 — 자리 단위로 보면 어떤 자리가 무조건 우월하다는 잘못된 결론에 빠진다.


What — 축별 비교

6축 전체 비교표

브라우저 웹Electron네이티브
실행 단위tab (origin 격리)BrowserWindow (process per window)OS 창 (in-process)
OS API 접근sandbox 안의 Web API만Node fs + Electron API + 네이티브 모듈플랫폼 SDK 전부
보안 모델same-origin policy + permission promptcontext isolation + sandbox + preload bridgeOS의 권한 모델 (entitlement·UAC 등)
배포URL — 즉시 갱신, 설치 없음서명된 binary + auto-update서명된 binary + 스토어·MSI·.app
시작 비용브라우저는 이미 떠 있음 (즉시)매번 새 Chromium + Node (500ms수초)매번 OS 프로세스 (~ms 단위)
메모리탭당 50150MB (브라우저가 공유 비용 흡수)빈 앱부터 90130MB, 창마다 +50~80MB보통 3080MB (UI 토킷마다 다름)
번들 크기0 (URL만)150300MB (Chromium 포함)550MB (정적 링크 시)
UI 통합OS 룩앤필 안 됨 — 브라우저 안OS 창·메뉴·트레이는 OK / Chromium 페이지는 웹 룩OS 룩앤필 그대로
권한 모델origin 단위 + prompt프로세스 분리 + IPC 통로OS의 user/group + capability
공격면XSS · CSRF · 클릭재킹위 전부 + XSS → OS 명령 위험메모리 안전성 · injection · privilege escalation
갱신즉시 (URL이 새 코드)사용자 동의 + 재시작사용자 동의 + 재시작 + 설치 권한
코드 공유1 코드 → 모든 OS1 코드 → 모든 OS보통 N 코드 (cross-platform 토킷 없으면)
개발 속도매우 빠름빠름느림
런타임 의존사용자 브라우저앱 자체에 동봉OS 동봉 SDK
오프라인어려움 (Service Worker로 일부)자연스러움자연스러움

축 1 — OS API 접근

작업브라우저 웹Electron네이티브
임의 경로 파일 읽기·쓰기불가 (File System Access API는 user gesture + prompt)OK (fs로 자유)OK
트레이/메뉴바 아이콘불가Tray플랫폼 API
글로벌 단축키불가globalShortcut플랫폼 API
시스템 알림OK (Notification)OKOK
클립보드일부 (gesture 필요)OK (clipboard)OK
자식 프로세스 실행불가child_process.spawnOK
네이티브 GUI 위젯불가 (HTML만)불가 (Chromium 안 — HTML만)OK
Bluetooth · USBWeb Bluetooth/USB API (prompt)OKOK
Camera · MicOK (prompt)OKOK

→ Electron은 OS API 접근에서 네이티브와 거의 동등이다. 단, 네이티브 GUI 위젯은 못 쓴다 — 그 안의 UI는 Chromium이 그리는 HTML이다. macOS의 NSTableView를 진짜로 그리고 싶다면 Electron이 아니다.

축 2 — 보안 모델

핵심: 브라우저 웹의 origin 경계공격이 origin 안에 갇히게 한다. XSS가 일어나도 그 도메인의 데이터가 위험할 뿐 OS는 안전하다.

Electron의 경계는 프로세스 + bridge다. XSS가 일어나면 preload가 노출한 함수만큼이 위험하다. 그래서 bridge를 좁게 만드는 것이 보안의 핵심이고, 그 결과로 contextIsolation: true가 디폴트가 됐다. 자세히는 05-security.

네이티브는 OS 경계를 그대로 빌린다. macOS의 entitlement, Windows의 UAC가 그 자리.

축 3 — 배포 모델

웹은 URL을 갱신하면 끝. 사용자 동작 없음.

Electron은 binary가 사용자 디스크에 깔려 있다. 갱신하려면 새 binary를 사인하고 → auto-update 서버에 올리고 → 클라이언트가 받고 → 재시작. 이 사이클이 Chromium 6주 동기화와 맞물려 분기 단위 정기 업무가 된다. macOS는 notarization(Apple 서명 + 공증)이 추가로 필요하다. Windows는 EV code signingSmartScreen 신뢰를 얻는 사실상의 조건.

네이티브는 같지만 플랫폼별로 다른 채널. App Store 심사, Windows Store, 자체 MSI, .dmg, deb/rpm 패키지 등.

축 4 — 시작 비용 / 메모리

시나리오웹 (탭)Electron네이티브
Cold start즉시 (브라우저 떠 있음)500ms3s (Chromium + Node 띄움)50300ms
Warm start즉시200500ms즉시
빈 앱 메모리(브라우저 공통 비용에 포함)90130MB3080MB
창 1개 추가탭 1개 50150MBRenderer 1개 5080MB보통 거의 무료
디스크0150300MB550MB

→ Electron의 진짜 비용런타임을 매번 띄우는 것이다. 사용자의 브라우저는 이미 떠 있어서 비용을 공유받지만, Electron 앱은 매번 자기 Chromium을 띄운다.

VS Code 팀이 V8 snapshotshared process를 만든 이유가 여기 — 시작 시간을 수 초 → 1초 미만으로 줄였다. 자세히는 07-performance-memory.

축 5 — 권한 모델

작업브라우저 웹Electron네이티브
”이 사이트가 카메라를 쓰려고 함”디폴트 promptElectron의 setPermissionRequestHandler개발자가 결정OS prompt (TCC on macOS, UAC dialog 등)
“이 코드가 파일에 접근”sandbox 안의 API 제한Main이 자유 / Renderer는 IPC 거침프로세스 권한대로
”이 코드가 다른 origin 호출”CORS preflight디폴트 webSecurity: true로 CORS 적용 / 끄면 위험제한 없음

→ Electron에서 권한 prompt는 디폴트로 자동 거부된다. setPermissionRequestHandler내가 직접 결정해야 한다 — 이 점이 의외다. 카메라·마이크·알림·지오로케이션 등이 prompt가 안 떠서 안 됨으로 디버깅된다.

축 6 — UI 통합

측면Electron네이티브
OS 창 chrome (제목 표시줄·트래픽 라이트)없음BrowserWindow 옵션으로 표시/숨김디폴트로 OS 룩
네이티브 메뉴바없음Menu API디폴트
다크 모드 자동 추종media querynativeTheme.shouldUseDarkColors + media query디폴트
접근성 (스크린리더)Chromium의 a11y tree → OS같음네이티브가 가장 깊음
폰트 렌더링ChromiumChromium (같음)OS 텍스트 시스템
입력 메서드 (CJK IME)ChromiumChromiumOS native — 가장 깔끔

OS의 진짜 룩앤필이 필요하면 네이티브. OS 창 chrome 정도면 충분하면 Electron으로 흉내낼 수 있다. 그 안의 콘텐츠는 어차피 HTML이라는 사실은 변하지 않는다.

결정 트리

→ 보통 공개 API·문서·간단 도구는 웹, 생산성 도구·메신저·노트·IDE는 Electron, 그래픽 성능이 핵심인 앱은 네이티브.


What-if — 잘못 쓰면

1) “Electron인데 웹처럼 짠다”

증상: Renderer에서 fetch('https://api.example.com')을 했더니 CORS 차단. localStorage각 BrowserWindow마다 다르게 보임. 원인: 브라우저 직관으로 Electron을 쓰는 경우. Renderer는 Chromium 페이지고, CORS·storage 격리가 그대로 적용된다 — 단, 세션이 독립이라 storage가 origin이 같아도 BrowserWindow마다 다르게 동작할 수 있다. 대응: 데이터 fetching·storage는 Main이 책임지고 Renderer는 IPC로 받는다. CORS는 Main에서 직접 호출하거나, session.webRequest로 헤더 조작.

2) “네이티브 룩앤필이 필요한데 Electron으로 선택”

증상: macOS의 NSToolbar·Sidebar·Inspector 같은 진짜 네이티브 위젯이 필요한데 HTML로 흉내내려다 어색함이 누적된다. 원인: Electron의 UI는 Chromium이 그리는 HTML. OS 네이티브 위젯이 아니다. 대응: 네이티브 룩이 제품의 핵심 가치면 그 부분은 네이티브로. 또는 cross-platform 토킷(Qt·Avalonia)으로.

3) “PWA로 충분한데 Electron으로 갔다”

증상: 200KB 웹앱을 200MB Electron으로 패키징. 사용자가 binary 다운로드를 거부. 원인: OS API가 없거나 거의 없는 앱에 Electron은 과잉 인프라. PWA가 설치 가능한 웹으로 같은 효과를 줄 수 있다. 대응: 결정 트리의 Q1을 솔직하게 답한다. PWA의 Web App Manifest + Service Worker로 풀리면 그쪽이 정답.

4) “Electron이니까 모든 게 동기”라고 보면

증상: Renderer에서 ipcRenderer.invoke('read-file')을 했는데 수백 ms 지연. UI 멈춤. 원인: IPC는 비동기 메시지 패싱. Main의 fs.readFile 호출 + 직렬화 + 메시지 큐 왕복이 수십~수백 ms. 대응: IPC를 비동기로 짠다. UI는 낙관적으로 먼저 그리고, 결과가 오면 갱신. 잦은 IPC는 배치하거나 MessagePort로 직접 채널을 만든다.

5) “보안은 나중에 본다”고 미루면

증상: 외부 URL 로딩 한 번에 preload가 노출한 함수가 RCE 통로가 됨. 원인: Electron의 보안은 디폴트 설정의 누적이다. 한 번 잘못 켜둔 옵션을 나중에 끄기깨진 기능을 다시 만드는 일과 같다. 대응: 설계 단계에서 contextIsolation: true · sandbox: true · webSecurity: true · CSP 명시 · 외부 URL 차단을 함께 켠다. 자세히는 05-security.


Insight — 흥미로운 이야기

Origin은 브라우저의 발명이지 인터넷의 본질이 아니다”

브라우저의 same-origin policy는 1995년 Netscape가 임시 보안 모델로 만든 게 영구화된 것이다. URL이 같으면 같은 신뢰 도메인이라는 발상은 웹이 문서 중심이던 시절에 만들어졌고, 오늘의 SPA·SaaS 시대에는 너무 거칠다. 그래서 CORS·CSP·SameSite·CSRF token 같은 수십 개의 보완책이 누적됐다.

Electron은 origin그대로 빌리되 — 의미를 다시 정의했다. Electron 앱의 root URLfile://이거나 app://이거나 custom protocol일 수 있고, 그 origin이 Renderer의 보안 경계가 된다. 하지만 진짜 경계preload bridge다 — origin은 내부 격리를 위한 도구일 뿐. 이 경계 재정의가 Electron의 핵심 디자인 결정이다.

”PWA의 한계가 Electron이 살아 있는 이유”

PWA(Progressive Web App)는 “설치 가능한 웹앱” — Service Worker로 오프라인, Web App Manifest로 OS 통합, File System Access API로 일부 파일 접근까지 닿는다. 이론적으로는 Electron을 대체할 수 있어야 한다.

실제로는 그렇지 못하다. 이유는 API 분포의 차이다.

기능Web APIElectron API
임의 경로 파일File System Access API (Chrome/Edge만, prompt 필요)fs 자유
트레이/메뉴바없음Tray
글로벌 단축키없음globalShortcut
자식 프로세스없음child_process
네이티브 모듈없음N-API

PWA는 공통 API의 보수적 확장인 반면 Electron은 Node API 전부를 들고 있다. VS Code 같은 IDE가 PWA로 못 가는 이유 — 자식 프로세스(tsc·eslint)·임의 파일 시스템·네이티브 git이 필요하다.

”Electron이 Chromium에 의존하는 게 약점인가 강점인가”

약점으로 보는 시각: Chromium 한 종류만 쓰니 엔진 다양성이 없다, 메모리 비용이 크다, Apple/Google의 정책에 묶인다.

강점으로 보는 시각: Chromium의 세계 최고급 보안 엔지니어링공짜로 빌린다, 모든 OS에서 같은 렌더링 결과가 나온다 (네이티브 webview는 OS마다 다름), DevTools가 그대로 작동한다.

같은 사실이 두 평가를 받는다 — 이것은 Electron이 위치한 자리근본적인 트레이드오프임을 보여준다. 한쪽 끝(Tauri는 OS webview)은 binary는 작고 메모리는 적지만 OS 호환성 차이가 발생, 다른 쪽 끝(네이티브)은 최고 성능이지만 N번 짠다. Electron은 통일된 Chromium을 비용으로 사고 코드 공유를 얻는다.


요약

세 자리가 6축에서 다르게 트레이드오프된다. 웹은 격리·즉시 갱신을 OS 접근 불가의 대가로, 네이티브는 OS 전부를 N-번 코드의 대가로, Electron은 둘의 사이에서 공유 코드 + OS 접근을 받고 격리·배포·보안 책임을 진다. Electron을 고르는 것은 제3의 길을 고르는 게 아니라 그 책임을 받아들이는 것이다. 다음 챕터들은 그 책임을 어떻게 운영하는가의 디테일이다.

다음 문서: 04-mental-model — 그래서 Electron 앱 한 개는 실제로 어떻게 생겼나. main.js + preload.js + index.html 세 파일로 시작한다.