Skip to content

✍️ 필사 모드: Tauri 2 — 本当にElectronを置き換えるのか、それとも別の道具なのか

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

プロローグ — なぜ今、また「デスクトップフレームワーク」の話なのか

「Electronは重い」という言葉は使い古されすぎてもはや陳腐だ。Slackを起動するとRAMが400MB消え、VS Codeは1GBに達し、Discordのインストーラーは90MBある。毎回、同じChromiumのコピーがユーザーのディスクとメモリを占拠する。

代替手段はずっと語られてきた。ネイティブで書く(人員が二倍)、Flutterデスクトップ(まだ不慣れ)、Qt(商用ライセンス)、そして — Tauri。

2024年10月、Tauri 2.0がGAになった。2026年現在、リリースから一年半が経過している。約束は強かった。10MBのバンドル、システムWebView、Rustコア、iOSとAndroid対応。紙の上では、Electronの席を奪うように見えた。

実際は? Slackは依然としてElectronだ。VS CodeもElectron。DiscordもElectron。大手アプリが移行しない理由がある。同時に、SilentKeys、Voxly、Watson.ai、Pakeといった新しいアプリは最初からTauriを選んでいる。新規プロジェクトと既存プロジェクトで判断が分かれている。

この記事はその分岐を整理する。アーキテクチャの本当の違い、測定可能なトレードオフ(バンドル、メモリ、起動時間)、システムWebViewが持ち込む影、セキュリティモデルの仕組み、モバイル対応の現実、そして「我々のチームに合うのか」を判断する基準。ElectronとTauriのどちらが万人にとっての正解、という記事ではない。

一行の要点: TauriはElectronを「軽くしたもの」ではなく、別のトレードオフを選んだ別の道具だ。そのトレードオフを知れば選択は容易になる。


1章 · アーキテクチャ — 何がどう違うのか

Tauriの差別化は一行で言える。Chromiumを同梱しない。代わりにOSのシステムWebViewを使う。

Electronアプリの構成
┌────────────────────────────────────────────────┐
│  あなたのアプリ                                  │
│  ├─ メインプロセス: Node.js ランタイム            │
│  ├─ レンダラープロセス: Chromium (V8 + Blink)    │
│  └─ Electron バイナリ (約80〜120MB)              │
│                                                │
│  → 同じOSに同じChromiumのコピーがN個存在する     │
└────────────────────────────────────────────────┘

Tauriアプリの構成
┌────────────────────────────────────────────────┐
│  あなたのアプリ                                  │
│  ├─ Rust コア (約3〜5MB)                         │
│  ├─ システムWebView呼び出し:                     │
│  │    macOS  → WKWebView (Safari/WebKit)        │
│  │    Windows → WebView2 (Edge/Chromium)        │
│  │    Linux  → WebKitGTK                        │
│  └─ Webアセット(HTML/JS/CSS)                     │
│                                                │
│  → OSがすでに持っているWebViewを借りる           │
└────────────────────────────────────────────────┘

違いの帰結:

  • バンドルサイズ: Electron 80〜200MB → Tauri 2〜40MB(多くは5〜15MB)
  • メモリ: Electron 200〜400MB アイドル → Tauri 30〜80MB アイドル
  • 起動時間: Electron 1〜3秒 → Tauri 0.2〜0.8秒
  • レンダリングの一貫性: Electronは同一、TauriはOSごとに別エンジン(これが核心のトレードオフ)

Tauriのバックエンド言語はRust。バックエンドコード(ファイルシステム、ネットワーク、システムAPI呼び出し)をRustで書き、フロントエンド(React/Vue/Svelte/Solidなど)はそのままWeb技術で書く。両者はIPCでメッセージをやり取りする。Electronのmain/renderer構造と概念的には同じで、ただ「main」がNode.jsではなくRustになっている。

まとめ: Electronは「ブラウザとNodeを丸ごと持ち歩く」、TauriはOSが持っているWebViewを借りてバックエンドだけRustでコンパイルする。


2章 · バンドルサイズとメモリ — 測定できる違い

抽象的な比較はやめて数値で見る。2026年時点の公開ベンチマークと移行事例から集めた値だ。

バンドルサイズ(.dmg / .msi / .AppImage)

アプリの種類ElectronTauri比率
最小の「Hello World」85MB2.5MB34倍
中規模(エディタ、チャット)120〜160MB8〜15MB約12倍
重量級(IDEクラス)180〜250MB25〜40MB約7倍
インストーラ平均80〜150MB3〜10MB約20倍

メモリ(アイドル時)

アプリElectronTauri
空のウィンドウ1つ180〜250MB25〜45MB
小さなUI画面220〜300MB35〜60MB
中複雑度SPA300〜500MB60〜120MB

起動時間(コールドスタート、M2 MacBook Pro)

アプリElectronTauri
最小アプリ0.8〜1.5秒0.15〜0.35秒
中規模1.5〜3秒0.4〜0.9秒

この数値が意味するもの: バンドル・メモリ・起動時間は本当に違う。ある移行事例は「インストーラ95%縮小、メモリ70%削減」を報告している。測定可能な勝利だ。

ただし — これがすべてを決めるわけではない。次章の落とし穴を読んでほしい。


3章 · システムWebViewの影 — 「一度書けばどこでも動く」という嘘

Tauri最大の弱点は、最大の強みから生まれる。システムWebViewだから軽いが、システムWebViewだからOSごとにレンダリングが異なる。

同じReactコードを実行すると:
  macOS    → WKWebView (Safari)        — Safariのバグや非対応をそのまま引き継ぐ
  Windows  → WebView2 (Edge/Chromium)  — Chromiumなので最も互換的
  Linux    → WebKitGTK                 — 最も遅れている。最新CSS/JS APIの一部が未対応

これが何を意味するか:

  • backdrop-filter: macOS/Windowsでは問題ないが、WebKitGTKは部分対応のみ
  • :has() セレクタ: 2026年もWebKitGTKのバージョンによってばらつく
  • OffscreenCanvas: macOS/Windows はOK、Linuxはケースバイケース
  • WebRTC、MediaRecorder: プラットフォームごとにコーデックと挙動が違う
  • フォントレンダリング: macOSのアンチエイリアスはWindows/Linuxと別物に見える
  • ドラッグ&ドロップ、Clipboard API: 各WebViewの癖がそのまま表れる

Electronは定義上この問題を持たない。どこで動かしても同じChromiumなので同じピクセルが出る。高価だが一貫性を買っている。

Tauriで書く現実のコードはこうなる。

/* Linux WebKitGTK 用のフォールバックを想定する */
.frosted {
  background: rgba(255, 255, 255, 0.85);
}

@supports (backdrop-filter: blur(10px)) {
  .frosted {
    background: rgba(255, 255, 255, 0.5);
    backdrop-filter: blur(10px);
  }
}

Linuxユーザーが少ないアプリなら無視してよい。だが開発者ツールを作るならLinuxユーザー比率が30〜40%になり得る。すると新しいCSS/JS機能のたびにフォールバックを書く必要が出てくる。「Chromiumで動くから終わり」は通用しない。

まとめ: Tauriは「一度書けばどこでも動く」ではなく「一度書いて3つのOSでテストする」。ElectronよりQAコストがかかる。


4章 · セキュリティモデル — allowlistからcapabilitiesへ

Tauri 2の大きな変更点の一つがセキュリティモデルだ。v1のallowlistは単純なオン/オフトグルだった — 「fs APIを有効にするか否か」。これが限界に達したので、v2はpermissions / scopes / capabilitiesの3層に再設計された。

3つの概念

  • Permissions: Tauriコマンドのオン/オフ(例: fs:allow-read-file)
  • Scopes: コマンドのパラメータ検証(例: 「このパスパターンのみ許可」)
  • Capabilities: 権限とスコープをウィンドウ/WebViewに紐付けて付与

これらをJSONかTOMLで宣言する。src-tauri/capabilities/ディレクトリにファイルを置けば自動で有効になる。

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "メインウィンドウに付与する権限の束",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:allow-read-text-file",
    "fs:allow-write-text-file",
    {
      "identifier": "fs:scope",
      "allow": [
        { "path": "$APPDATA/notes/*" },
        { "path": "$DOCUMENT/MyApp/**" }
      ],
      "deny": [
        { "path": "$HOME/.ssh/*" }
      ]
    },
    "shell:allow-open"
  ]
}

この設定が意味するもの:

  • メインウィンドウだけがこれらの権限を受け取る(他のウィンドウは別capability が必要)
  • ファイルシステムの読み書きは有効 — ただし$APPDATA/notes/$DOCUMENT/MyApp/配下のみ
  • $HOME/.ssh/*は明示的に拒否
  • shell:openは許可(メール/URLを開くため)

Electronにはこのような宣言的モデルがない。Electronではセキュリティはコードパターンだ — contextIsolation: truenodeIntegration: false、preloadスクリプト、IPCホワイトリストを手で書く必要がある。忘れればRCE脆弱性になる。

Tauriのモデルがより安全な理由:

  1. 宣言的: 権限がコードではなく設定なので監査が容易
  2. きめ細かい: ファイルシステム全体ではなく特定パスのみ
  3. デフォルト拒否: capability を明示しないIPCコマンドはブロックされる
  4. ウィンドウ別: メインウィンドウとaboutウィンドウの権限を分離

もちろんElectronも気を配って書けば同等になる。違いは — Tauriではそれがデフォルトであり、Electronでは追加作業であるという点。

まとめ: capabilitiesは「デフォルトで安全なIPC」を強制する。Electronで気を抜くとRCEになるパターンが、Tauriではcapabilityファイルを書かなければIPCが単に拒否される。


5章 · IPC契約 — コマンド一つを最初から最後まで見る

抽象的な話はやめて、実際のIPCコマンドを一つ最初から最後まで見よう。シナリオ: フロントエンドで「ノート保存」ボタンを押すと、バックエンド(Rust)がディスクに書き込む。

Rust側: コマンド定義

src-tauri/src/commands.rs:

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tauri::AppHandle;
use tauri_plugin_fs::FsExt;

#[derive(Debug, Serialize, Deserialize)]
pub struct Note {
    pub id: String,
    pub title: String,
    pub body: String,
}

#[derive(Debug, Serialize)]
pub struct SaveResult {
    pub bytes_written: usize,
    pub path: String,
}

#[tauri::command]
pub async fn save_note(
    app: AppHandle,
    note: Note,
) -> Result<SaveResult, String> {
    // 1) 入力検証 — IPC境界は信頼できない
    if note.id.is_empty() || note.id.contains('/') || note.id.contains('\\') {
        return Err("invalid note id".into());
    }
    if note.body.len() > 10 * 1024 * 1024 {
        return Err("note too large (>10MB)".into());
    }

    // 2) capability が許可するパス配下でのみ作業
    let app_data = app
        .path()
        .app_data_dir()
        .map_err(|e| format!("no app data dir: {e}"))?;
    let mut path: PathBuf = app_data.join("notes");
    std::fs::create_dir_all(&path).map_err(|e| e.to_string())?;
    path.push(format!("{}.json", note.id));

    // 3) シリアライズして書き込む
    let body = serde_json::to_vec_pretty(&note).map_err(|e| e.to_string())?;
    let bytes = body.len();
    std::fs::write(&path, body).map_err(|e| e.to_string())?;

    Ok(SaveResult {
        bytes_written: bytes,
        path: path.display().to_string(),
    })
}

src-tauri/src/lib.rs:

pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
        .invoke_handler(tauri::generate_handler![
            commands::save_note,
        ])
        .run(tauri::generate_context!())
        .expect("tauri 起動失敗");
}

TypeScript側: 呼び出し

src/lib/notes.ts:

import { invoke } from '@tauri-apps/api/core'

export interface Note {
  id: string
  title: string
  body: string
}

export interface SaveResult {
  bytesWritten: number
  path: string
}

export async function saveNote(note: Note): Promise<SaveResult> {
  // invoke は Rust 側の #[tauri::command] 名と 1:1 でマッピングされる
  // シリアライズは自動 (serde と JSON の間)
  // ただし snake_case と camelCase の対応規則は確認が必要
  const raw = await invoke<{ bytes_written: number; path: string }>('save_note', {
    note,
  })
  return { bytesWritten: raw.bytes_written, path: raw.path }
}

Reactコンポーネント内で:

import { useState } from 'react'
import { saveNote } from './lib/notes'

export function NoteEditor() {
  const [title, setTitle] = useState('')
  const [body, setBody] = useState('')
  const [status, setStatus] = useState<string | null>(null)

  async function onSave() {
    try {
      const result = await saveNote({
        id: crypto.randomUUID(),
        title,
        body,
      })
      setStatus(`保存完了: ${result.path} (${result.bytesWritten} bytes)`)
    } catch (e) {
      setStatus(`失敗: ${String(e)}`)
    }
  }

  return (
    <div>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea value={body} onChange={(e) => setBody(e.target.value)} />
      <button onClick={onSave}>保存</button>
      {status && <p>{status}</p>}
    </div>
  )
}

なぜこれが安全か

  1. capability で明示的に許可src-tauri/capabilities/main.jsonsave_noteが含まれるウィンドウのみ呼び出し可能
  2. Rust側で入力検証 — id にスラッシュがあればディレクトリ脱出の試み、サイズ制限も明示
  3. パス限定app_data_dir()配下のみで作業、ユーザーが任意のパスを注入できない
  4. 型安全 — Rust側はコンパイラが、TS側はinvokeのジェネリクスが検証

Electronで同じものを書こうとすると、preload + contextBridge + IPCハンドラ + 明示的なホワイトリストを自分で組み立てる必要がある。コード量は同程度だが、忘れるとセキュリティホールになる。

まとめ: TauriのIPCはRustの型システムとcapabilityシステムが一緒に強制する。セキュリティがコードの隣に置かれている。


6章 · モバイル — Tauri 2の本当に大きな変化

Tauri 1はデスクトップのみだった。v2の発表で最大の見出しだったのがiOSとAndroid対応だ。2026年時点で、これがどこまで現実になっているかが重要になる。

何ができるか

  • iOSとAndroidのビルドが安定(Tauri 2.0 GAから)
  • 同じWebフロントエンドのコードをデスクトップと共有
  • Rustバックエンドをモバイルでも呼び出し可能(ネイティブプラグインはSwift/Kotlin/Javaで記述可能)
  • 基本プラグイン(ファイルシステム、通知、ダイアログなど)の多くがモバイルで動作

まだ粗い部分

  • デスクトッププラグイン = モバイルプラグインではない。一部のプラグインはデスクトップ専用、一部はモバイル専用
  • Tauriチーム自身が認めている — 「モバイルを第一級市民に」という約束は過大だった、コミュニティと共に作っていくとGA振り返りに書かれている
  • iOSのサイドローディングの難しさ(Appleのポリシー)、Androidは比較的自由
  • FlutterやReact Nativeほどモバイル向けに最適化されたUXを自動では提供しない — どのみちWebViewベースなので、モバイルネイティブの感触を出すには追加作業が必要

いつTauriモバイルを選ぶか

シナリオ推奨
すでにTauriデスクトップアプリがあり、同じコードをモバイルに拡張Tauriモバイルが良い
モバイル優先のアプリ、デスクトップは副次React Native / Flutter / ネイティブ
デスクトップとモバイルの両方が一級、コード共有が重要Tauriを真剣に検討
モバイルネイティブのUXが本質(ジェスチャ、アニメーション)ネイティブまたはFlutter

まとめ: Tauriモバイルは「デスクトップアプリのモバイル同伴者」としては優秀、「モバイル優先アプリの基盤」としてはまだReact Native / Flutterほど熟成していない。


7章 · Electron · Wails · Neutralino · ネイティブ — 比較マトリクス

各ツールの居場所を整理する。

項目Tauri 2ElectronWailsNeutralinoネイティブ
バックエンド言語RustNode.jsGoC/C++(コア)Swift/Kotlin/C++/C#
レンダリングシステムWebViewChromium 同梱システムWebViewシステムWebViewネイティブUI
バンドルサイズ5〜40MB80〜200MB10〜25MB2〜10MB1〜20MB
メモリアイドル30〜80MB200〜400MB50〜100MB30〜70MB20〜80MB
モバイルiOS/Androidなしなしなし第一級
レンダリング一貫性低(OS依存)
学習曲線高(Rust)低(JS)中(Go)最高
セキュリティモデルcapabilities(宣言的)コードパターンメソッドバインドallowListOS API
成熟度高(2024 GA)非常に高最高
エコシステム急成長巨大OS依存
後援財団(独立)OpenJSコミュニティコミュニティプラットフォーム提供者

各ツールの一行サマリ

  • Tauri 2: 軽量で安全なデスクトップ・モバイルアプリ。Rustを学ぶ意志があるならベスト。
  • Electron: 最も広いエコシステム、一貫したレンダリング。重量を受け入れる代わりに素早い開発。
  • Wails: Goチームの自然な選択。Goのシンプルさをそのままデスクトップに持ってくる。ただし一部機能(マルチウィンドウなど)はまだ進行中。
  • Neutralino: 超軽量。コンパイル不要でNodeに近いAPI。最も軽いが最も未成熟。
  • ネイティブ: プラットフォームごとに一チーム。最高のUX、最も高価。

8章 · Rust学習曲線 — フロントエンドチームの本当のコスト

Tauriの最大の参入障壁はバンドルサイズでもシステムWebViewでもない。Rustだ。フロントエンド中心のチームにRustを導入するコストを正直に評価しよう。

よい知らせ

  • Tauriは「フロントは普段通り + バックエンドだけRust」という構造だ。バックエンドコードが少ない単純なアプリなら、Rustコードは数個のIPCコマンドとmain.rs程度で済む
  • Tauri CLIがボイラープレートの約90%を生成してくれる — Rustのビルドシステム(cargo)、クロスコンパイル、コード署名設定の大半が抽象化されている
  • ChatGPT/Claudeなどのツールがコード生成を上手にこなす。単純なIPCコマンドはほぼAIが書いてくれる
  • バックエンドが単純であれば、Rustの難しい部分(ライフタイム、深い非同期、unsafe)にほとんど触れない

よくない知らせ

  • バックエンドが複雑になった瞬間 — DBコネクションプール、非同期処理、並行性 — Rustの学習曲線が急になる
  • コンパイル時間が長い。初回ビルド5〜15分、増分ビルド5〜30秒。Electronは即時リロード
  • エラーメッセージは親切だが量が多い。JSの一行エラーがRustでは30行のトレイト境界の説明になり得る
  • チーム内でRustコードをレビューできる人が一人だけだと、その人がボトルネックになる
  • 依存(crate)のセキュリティ監査。cargo auditはあるが、JSのnpm周辺よりツールは少なく、一部のcrateはunsafeを多用する

現実的な判断基準

チームの状況推奨
フロント専任、Rust経験なし、バックエンドが単純Tauriを試す価値あり
フロント専任、バックエンドが複雑(DB・並行性)Electron + Node か、Tauri + サイドカー
Rust経験のあるメンバーが一人以上Tauri強く推奨
システムプログラミング背景ありTauriが自然に合う
6ヶ月以内に出荷、スケジュール厳しいElectron — すべて検証済み

興味深いパターン: 多くのチームがサイドカーパターンで折衷する。Tauriメインは軽く保ち、重いバックエンドロジック(例: Python機械学習、Nodeサービス)は別プロセスとして起動しTauriに管理させる。これでRust学習コストを最小化しつつ、Tauriのバンドル・メモリの利点は確保できる。

まとめ: フロントエンドチームにとってRustは本当のコストだ。バックエンド複雑度が低ければ耐えられるし、高ければ別のツールの方がよい。


9章 · 実際にTauri 2で作られたアプリ

2026年時点でTauri 2でリリースされた注目のアプリ。名前とカテゴリ、そしてなぜTauriを選んだかの推測。

アプリカテゴリなぜTauriか(推測)
SilentKeysプライバシー優先の音声書き起こしオンデバイスML、小さなバンドル、システム統合
VoxlyAI音声書き起こし高速起動、低メモリ、システムトレイ統合
Watson.ai会議の録音・抽出バックグラウンド動作、システム権限のきめ細かい制御
PakeWebページをデスクトップアプリへ小さなバンドルが核心の価値(Electronより明確な勝ち)
RivetAIエージェントのビジュアルプログラミング重いIDE的UX、メモリ節約が重要
XGetter動画・音声ダウンローダシステム統合、小さなインストーラ
Kunobi / KubeliKubernetesデスクトップクライアント開発者ツール、高速起動、MCPサーバー内蔵

共通パターンが見える:

  1. 新規プロジェクト — 既存のElectronアプリの移植ではなく、最初からTauriを選択
  2. 小〜中規模 — 巨大IDEではなく、単一目的が明確なアプリ
  3. システム統合が重要 — トレイ、バックグラウンド、グローバルショートカット、システム権限
  4. メモリに敏感 — バックグラウンドで動き続けるアプリ(書き起こし、会議録音など)
  5. 開発者やパワーユーザー対象 — ユーザーのLinux比率がある程度ある

逆に移行事例が少ない理由:

  • 既存Electronアプリのコード資産が大きい — Rustでバックエンドを書き直すコスト
  • ユーザーがすでに満足している — 壊れていないものを直すな
  • Linuxユーザー比率が低ければシステムWebViewの差を気にしなくてよい

まとめ: Tauriは「新規プロジェクトで意識的に選択される」ケースが圧倒的だ。既存Electronアプリからの移行は強い動機がある場合のみ正当化される。


10章 · いつ何を選ぶか — 意思決定ツリー

スタート:
├─ モバイルが優先か?
│   ├─ はい → React Native / Flutter / ネイティブ
│   └─ いいえ → 次へ
├─ Linuxを真剣にサポートすべきか?
│   ├─ はい、Linuxユーザー30%以上 → システムWebViewの差を許容できるか?
│   │       ├─ はい → Tauri / Wails
│   │       └─ いいえ、ピクセル一貫性が必要 → Electron
│   └─ いいえ、少ない → 次へ
├─ チームにRust経験があるか?
│   ├─ はい、または学ぶ意志あり → Tauri強く推奨
│   ├─ Go経験あり → Wails検討
│   └─ JSだけ、学ぶ時間なし → Electron
├─ バンドルサイズ・メモリが本当に重要か?
│   ├─ はい(バックグラウンドアプリ、低スペック端末) → Tauri
│   └─ いいえ、ユーザーは気にしない → Electron が速い
├─ バックエンドロジックが複雑か?
│   ├─ はい、DB・並行性多い → Electron か、Tauri + サイドカー
│   └─ いいえ、主にUI → Tauri か Neutralino
└─ スケジュールが厳しいか?(3〜6ヶ月)
    ├─ はい → Electron(すべて検証済み)
    └─ いいえ、1年以上 → Tauri学習コストが回収可能

簡単なルール

  • 新規プロジェクト、単純なバックエンド、バンドル重要 → Tauri
  • 既存Electronアプリ、ユーザー満足 → そのまま維持
  • AAA級デスクトップUX、Linuxが第一級市民 → Electron
  • デスクトップ + モバイルのコード共有が重要 → Tauri か Flutter
  • Goチーム → Wails
  • 1MBでも削りたい → Neutralino(ただし未成熟)
  • OSごとに最高のUX、人員無限 → ネイティブ

11章 · アンチパターンと落とし穴

Tauriを選んで後悔するパターン。

「Electronのように書けばよい」

  • 症状: Rustバックエンドを巨大な単一ファイルにして、すべてのIPCコマンドをそこに詰める
  • 結果: コンパイル時間爆発、変更コスト増大、テスト不可
  • 代わりに: Rustモジュールでドメインを分離(commands/services/models/)、コマンドは薄く(検証 + サービス呼び出し)

「capabilityを全部オンに」

  • 症状: core:defaultに加えて、すべてのfs/shell/dialog権限をワイルドカードでオン
  • 結果: セキュリティモデルの核心価値を捨てる。Electronのリスクをそのまま受け取る
  • 代わりに: 本当に必要なものだけ、スコープを狭く。ウィンドウ別にcapabilityを分離

「最初からRust非同期を深く」

  • 症状: 単純なIPCをtokio::spawnとチャンネル6個で実装
  • 結果: 学習曲線爆発、デッドロック、コンパイルエラーの沼
  • 代わりに: 最初はasync fnとTauriのデフォルトランタイム。非同期が本当に必要なときだけ深く

「システムWebViewの差を無視」

  • 症状: macOSだけで開発、リリース直前にLinuxテスト
  • 結果: WebKitGTKでUIの半分が壊れる
  • 代わりに: 最初の週から3 OSのCIでビルド、主要画面はすべてのOSでスクリーンショット比較

「フロントもRustで書く」

  • 症状: Leptos/Yew/Dioxusでフロントまでもすべてに
  • 結果: Rust学習曲線が二倍、エコシステムはReact/Vueの百分の一
  • 代わりに: フロントは普段のツール(React/Vue/Svelte)、バックエンドだけRust。Tauriの設計はそうなっている

「AIエージェントにTauriアプリを丸ごと作らせる」

  • 症状: エージェントがcapability設定とRust IPCパターンを甘く作り、セキュリティホール
  • 結果: 権限が広すぎる、入力検証が抜ける、サイドチャネルが開く
  • 代わりに: capabilityとIPCコマンドは人がレビュー、単体テストはIPC境界の境界条件を明示的に強制

12章 · 移行 — ElectronからTauriへ移すなら

移行が正当化されるシナリオがあるなら、手順は次のようになる。

1. 計測: 現在のElectronアプリのバンドル、メモリ、起動時間を記録
2. バックエンド棚卸し: Node依存をすべて列挙 — どれがRust crateに置き換わるか?
3. フロント分離: UIはそのまま持っていけるか(ほとんどの場合可能)
4. IPCマッピング: 現在のipcMainハンドラを一つずつTauriコマンドにマップ
5. capability設計: 各IPCが何にアクセスするかを整理 → capabilitiesファイル
6. システム統合: トレイ/通知/ショートカットをTauriプラグインで再実装
7. クロスOSテスト: 初日から3 OSでビルド、視覚回帰テスト
8. 計測再確認: 約束された数値(メモリ/バンドル)が実際に出るか検証
9. ベータユーザー → 段階的リリース

移行すべきでないシグナル

  • Node依存にRust代替がないものが多い(特定の機械学習ライブラリなど)
  • ユーザーの70%がmacOS、システムWebViewの差はそもそも問題にならず、メモリも満足
  • チームにRustが書ける人が0人、学ぶ6ヶ月もない
  • アプリがユーザーにとって十分速く、バンドルサイズへの不満がない

移行が正当化されるシグナル

  • バックグラウンドで24/7動くアプリで、メモリが実際にユーザーマシンに負担
  • インストーラサイズがダウンロード転換率に影響(低スペック/低帯域市場)
  • セキュリティ監査を通る必要があり、Electronのセキュリティパターンを整理する方が高くつく
  • モバイル展開予定があり、デスクトップコードを再利用したい

エピローグ — チェックリスト、アンチパターン、次回予告

Tauri 2は「Electronより軽い何か」ではなく、別のトレードオフを選んだ別の道具だ。システムWebViewの影、Rust学習曲線、小さなエコシステムを受け入れて — バンドル・メモリ・セキュリティモデルの利点を得る。新規プロジェクト、単純なバックエンド、バンドルが重要な状況なら合理的な選択だ。既存Electronアプリは強い動機がなければ移さなくてよい。

Tauri選択チェックリスト

  1. 新規プロジェクトか、既存の移行か? — 新規なら参入コストが正当化される
  2. バックエンドの複雑度は? — 単純なほどTauriが自然
  3. モバイル計画があるか? — あればTauri 2のモバイル対応が魅力
  4. チームのRust経験は? — 0人なら学習期間をスケジュールに足す必要
  5. Linuxユーザー比率は? — 高ければシステムWebView QAコストを計画に含める
  6. バンドル・メモリが本当に重要か? — バックグラウンドアプリ、低スペック端末対象なら利点
  7. スケジュールの余裕は? — 厳しければElectron、1年以上ならTauri学習回収可能
  8. capability設計を早めに始めたか? — セキュリティモデルがコード構造を左右する
  9. サイドカーパターンが必要か? — 重いバックエンドは別プロセスで分離
  10. 3 OSのCIを最初の週に組んだか? — 最後の週で発見すると遅い

アンチパターン

アンチパターンなぜ悪いか代わりに
バンドルサイズだけでTauri選択Rust学習コストとQAコストを無視チームとスケジュールも評価
Electronコードをそのまま1:1ポートRustのイディオムが違い不自然ドメインから再設計
capabilityをワイルドカードでオンセキュリティモデルの価値消滅本当に必要な権限だけ、スコープ狭く
Linuxテストを最後にリリース直前に発見最初の週から3 OS CI
フロントもRustで(Leptosなど)エコシステム百分の一フロントはReact/Vue、バックエンドだけRust
巨大なcommands.rs一つコンパイル時間爆発ドメイン別モジュールに分割
最初からRust非同期を深く学習曲線の崖同期から、必要なときだけ非同期
システムWebViewの一貫性を仮定macOSだけ見てリリースCSS・JS機能はフォールバック + 全OSの視覚回帰
モバイルをReact Native代替と期待モバイル優先UXまだ不足デスクトップの伴侶として使う
ROIなしに移行強行コード資産無駄、ユーザー影響測定可能な勝ちがあるときだけ

次回予告

次回は**「Rust非同期の落とし穴 — async/await、tokio、Sendトレイト、そしてライフタイムが交わる場所」**。今回が「Tauriを選ぶか」の記事なら、次回は「Tauriバックエンドが複雑になり始めると何に出会うか」の記事だ。Rust非同期は強力だが落とし穴が多い — Send/Syncトレイト境界、ライフタイムと非同期の相互作用、チャンネル選択(mpsc/oneshot/broadcast)、selectパターン、そして非同期コンテキストから同期コードを呼ぶ方法まで。Tauriバックエンドでも、バックエンドマイクロサービスでも、Rust非同期を真剣に使うあらゆる場所に適用される。


参考 / References

현재 단락 (1/405)

「Electronは重い」という言葉は使い古されすぎてもはや陳腐だ。Slackを起動するとRAMが400MB消え、VS Codeは1GBに達し、Discordのインストーラーは90MBある。毎回、同じ...

작성 글자: 0원문 글자: 16,864작성 단락: 0/405