--- id: mobile-tauri-capacitor title: Tauri / Capacitor — Web 기술 → Native category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [mobile, tauri, capacitor, electron, vibe-coding] tech_stack: { language: "TS / Rust / JS", applicable_to: ["iOS", "Android", "Desktop"] } applied_in: [] aliases: [Tauri, Capacitor, Electron, Ionic, web-to-native, system webview] --- # Tauri / Capacitor / Electron > Web tech (React / Vue) → native app. **Tauri = Rust + system webview (가벼움), Capacitor = Mobile 우선, Electron = Desktop only Chromium 포함 (큼)**. ## 📖 핵심 개념 - WebView 기반: 시스템 browser engine. - Native bridge: JS ↔ native 통신. - Bundle size: Tauri ~3MB / Electron ~150MB. - Performance: native < Tauri ≤ Capacitor ≤ Electron. ## 💻 코드 패턴 ### Tauri (Rust + Web) ```bash npm create tauri-app # 또는 기존 web 프로젝트에 npm install -D @tauri-apps/cli npx tauri init ``` ```rust // src-tauri/src/main.rs use tauri::Manager; #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}!", name) } #[tauri::command] async fn read_file(path: String) -> Result { std::fs::read_to_string(path).map_err(|e| e.to_string()) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet, read_file]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` ```ts // React 안 import { invoke } from '@tauri-apps/api/core'; const greeting = await invoke('greet', { name: 'Alice' }); const content = await invoke('read_file', { path: '/tmp/x' }); ``` ### Tauri Mobile (2.0+) ```bash npx tauri ios init npx tauri android init npx tauri ios dev npx tauri android dev ``` → Tauri 가 iOS / Android 도 지원 (2024+). ### Capacitor (Ionic) ```bash npm install @capacitor/core @capacitor/cli npx cap init npm install @capacitor/ios @capacitor/android npx cap add ios npx cap add android ``` ```ts import { Camera, CameraResultType } from '@capacitor/camera'; import { Geolocation } from '@capacitor/geolocation'; const photo = await Camera.getPhoto({ resultType: CameraResultType.Uri, quality: 90, }); const pos = await Geolocation.getCurrentPosition(); ``` ```bash npx cap sync # web build → native projects npx cap open ios # Xcode 열기 npx cap run ios # build + run ``` ### Capacitor plugin ```ts // plugins/MyPlugin.ts import { registerPlugin } from '@capacitor/core'; interface MyPlugin { doStuff(options: { value: string }): Promise<{ result: string }>; } const MyPlugin = registerPlugin('MyPlugin'); export default MyPlugin; ``` ```swift // ios/App/MyPlugin.swift import Capacitor @objc(MyPlugin) public class MyPlugin: CAPPlugin { @objc func doStuff(_ call: CAPPluginCall) { let value = call.getString("value") ?? "" call.resolve(["result": value.uppercased()]) } } ``` ### Electron (desktop only) ```bash npm install electron ``` ```js // main.js const { app, BrowserWindow, ipcMain } = require('electron'); app.whenReady().then(() => { const win = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: 'preload.js', contextIsolation: true, nodeIntegration: false }, }); win.loadURL('http://localhost:5173'); }); ipcMain.handle('read-file', async (event, path) => { return require('fs').readFileSync(path, 'utf8'); }); ``` ```js // preload.js const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('api', { readFile: (path) => ipcRenderer.invoke('read-file', path), }); ``` ### Bundle size 비교 ``` Hello world app: - Tauri: 2-5 MB - Capacitor: 8-15 MB (apk/ipa) - Electron: 120-200 MB (Chromium 포함) ``` ### Updates ```ts // Tauri updater import { check } from '@tauri-apps/plugin-updater'; const update = await check(); if (update?.available) { await update.downloadAndInstall(); } ``` ```ts // Capacitor — Live Update (Ionic) / 자체 OTA ``` ```ts // Electron — electron-updater import { autoUpdater } from 'electron-updater'; autoUpdater.checkForUpdatesAndNotify(); ``` ### Native API access ```ts // Tauri import { fs, dialog, notification } from '@tauri-apps/api'; await dialog.message('Hello'); await notification.sendNotification({ title: 'Done' }); // Capacitor import { Filesystem } from '@capacitor/filesystem'; import { Toast } from '@capacitor/toast'; import { Haptics } from '@capacitor/haptics'; ``` ### Webview tradeoffs ``` iOS WKWebView: + Native rendering - Apple WebKit engine 만 (Safari 같은 quirks) Android WebView: + Updateable (Play Store) - 옛 Android = 옛 webview engine Tauri = system webview = 일관성 변동. Electron = Chromium 포함 = 일관성 강 but 큼. ``` ### Security ```ts // Tauri allowlist // tauri.conf.json { "tauri": { "allowlist": { "fs": { "readFile": true, "writeFile": false }, "shell": { "open": true } } } } // Capacitor — 같은 native 권한 // Electron — contextIsolation: true + preload bridge ``` ### vs Native / RN / Flutter ``` Tauri / Capacitor: + Web tech reuse + Bundle 작은 (Tauri) - 일부 native API 어려움 - Webview rendering = 일부 jank Native: + Best UX / API access - Two codebases RN / Flutter: + Single codebase + native rendering - 학습 / 더 큰 ecosystem ``` ## 🤔 의사결정 기준 | 상황 | 추천 | |---|---| | 기존 web app → desktop | Tauri (가벼움) / Electron (큰 ecosystem) | | 기존 web → mobile | Capacitor / Tauri Mobile | | 새 cross-platform | Flutter / RN | | Native UX critical | Native | | Quick prototype + reuse code | Capacitor | | 매우 작은 bundle | Tauri | ## ❌ 안티패턴 - **모든 거 webview**: native UX 잃음. - **Bundle size 무시 (Electron 200MB)**: 사용자 다운로드 거부. - **Auto-update 없음**: 사용자 갱신 못 함. - **Permission 모두 allow**: 보안. allowlist. - **Native API + browser API 혼용**: 일관 X. - **Old webview 가정 + ES2023**: polyfill / target down. - **Electron contextIsolation false**: nodeIntegration = security 구멍. ## 🤖 LLM 활용 힌트 - Tauri = 가볍고 modern, Mobile 도 OK. - Capacitor = Ionic 있는 사용자. - Electron = legacy / 큰 desktop. - Web reuse > native UX 인 경우 적합. ## 🔗 관련 문서 - [[Mobile_Flutter_Patterns]] - [[Mobile_KMP_Compose]] - [[Web_PWA_Service_Worker]]