Problem
固定費を見たいだけなのに、家計簿アプリは重すぎる。
サブスクリプション型サービスの増加で、毎月の固定費は気づかないうちに膨らんでいく。ところが既存の家計簿アプリは 銀行連携・口座登録が前提 で、プライバシーへの不安と初期設定のコストが最初のハードルになる。
「月額いくら払っているか」だけを知りたいライトユーザーにとって、家計簿アプリはそもそもオーバースペック。必要なのは、入力は最小 / 気づきは最大 の、もっと素朴な道具だった。
同時に、個人的には サーバー維持費ゼロ・アカウント不要・データは自分のもの というローカルファースト Web アプリの実装パターンを、実アプリで検証したかった。
Approach
ブラウザ内で完結する、SQL データベース付き PWA。
バックエンドを持たない前提で設計を組み直した。永続化には公式 @sqlite.org/sqlite-wasm を採用し、SQLite WASM を OPFS に書き込む ブラウザ内データベースを本番運用。OPFS 非対応の環境 (Safari 等) には sqlite3.capi.sqlite3_vfs_find('opfs') で機能検出してから localStorage へ透過的にフォールバック する抽象化層を置いている。
ローカルファーストは「機能」ではなく運用ルールとして徹底。fetch / XMLHttpRequest / WebSocket を使うコードを内部ルールで明文化して禁止し、PR レビューでブロッカー扱いにしている。サーバーを持たないと決めた瞬間に、認証・同期・バックアップの責務の位置が根本から変わった。
状態管理は外部ライブラリを入れず React Context + useReducer のみ。サブスクは多くて数十件というドメインの規模感なら、Redux / Zustand を入れる複雑性のほうがコスト過多と判断した。スタイリングも Tailwind を入れず、CSS Variables だけで薄いデザインシステムを組む。
ダッシュボード
月額 / 年額の合計、カテゴリ別ドーナツチャート、クイック追加 FAB を 1 スクリーンに集約。
サブスク管理
追加・編集・スワイプ削除。30 以上のサービス名からカテゴリを自動推定してゼロコンフィグ化。
分析 & 節約シミュレーション
カテゴリ別ランキング + 「もし解約したら年間いくら浮くか」を 1 タップで試算。
バックアップ / 復元
JSON エクスポート・インポート。サイズ (5MB) / 件数 (1000) / スキーマ / 型 / 値域を全検証してから取り込む。
PWA & ダークモード
Workbox ベースの Service Worker でオフライン動作。OS 設定追従のテーマ切替と、ホーム画面追加に対応。
ローカル完結のセキュリティ
SQL は全クエリでパラメータバインド必須。インポート JSON はスキーマ・型・値域を検証してから取り込む。
Result
v1.0.0 を MIT で公開。
運用フェーズの心理負荷はほぼゼロ。
2026-04-04 に v1.0.0 をリリースし、ソースコードを GitHub で MIT ライセンス公開 している。外部 API・バックエンドを一切持たない完全クライアント完結の構成で、守るべき境界が最小化された結果、運用フェーズの精神的負荷はほぼゼロ。
ブラウザ単体 + SQLite WASM + OPFS という組み合わせでも、実用に耐える本格アプリ を作れることを実証できたのがこのプロジェクトの成果のひとつ。
Learnings
作ってみて分かったこと。
ローカルファーストは "機能" ではなく "設計ルール"
「データを外に出さない」を原則ではなく コードに書けないルール として明文化したのが効いた。fetch / XMLHttpRequest / WebSocket を PR レビューでブロックする運用にしてから、認証・同期・バックアップの設計が一気にシンプルになった。
OPFS の COOP/COEP 要件は配信側で片付ける
SharedArrayBuffer を使うため、配信側で Cross-Origin-Opener-Policy: same-origin と Cross-Origin-Embedder-Policy: require-corp を設定する必要がある。Vite 開発サーバーと本番配信でヘッダーを統一してしまえば、アプリ側のコードに侵入せずに解決できた。
Safari 対応は "機能検出 + 透過的フォールバック"
OPFS 非対応環境では sqlite3_vfs_find('opfs') が偽を返すので、そこを分岐点に localStorage 同期へ落とす。上位層から見ると同じインターフェースで書き続けられるよう、ストレージ抽象化は "薄すぎず、厚すぎず" の粒度に調整した。
サブスク規模なら外部状態管理ライブラリは要らない
数十件程度のドメインに Redux / Zustand を入れるのは、解決する問題より導入する複雑性のほうが大きい。React Context + useReducer で足りる領域を見極める練習になった。
テストのコスト配分は "DB × E2E の二段" で足りる
DB 層・hook・util は Vitest で単体検証し、Playwright は主要フロー (CRUD・バックアップ・テーマ切替) のみ E2E でカバー。個人開発でも回せる量に抑えつつ、壊れたら困る境界だけは確実に守れる構成になった。
"サーバーを持たない" 前提の精神的平穏
デプロイ後に障害・流出・課金のどれも気にしなくていい状態は想像以上に楽だった。守るべき境界が小さくなることの副次効果として、個人開発を継続するハードルが劇的に下がる。