開発の背景
サウナ愛好家にとって、日々の「ととのい」の記録は重要ですが、既存のサービスでは「サウナ室の温度」や「水風呂の滞在時間」「セットごとのコンディション」といった高解像度な数値を残すには限界がありました。
将来的にはこれらの記録をヘルスケアデータと連携させ、自分にとって最適なサウナ体験をデータドリブンに追求したい。この課題を解決するため、独自のサウナ記録アプリ「Sauny(サウニー)」の開発に着手しました。
しかし、個人開発における最大の敵は「完成前にモチベーションが尽きること」です。特にデータ連携を見据えた多機能なアプリを最初から目指すと、実装コストが膨らみリリース前に頓挫するリスクが高まります。
そこで今回は「1週間でMVP(Minimum Viable Product)をリリースし、素早く検証サイクルを回す」という戦略をとりました。アプリの審査待ちを避けつつ、ネイティブアプリと同等のUXを最速でユーザーに届けるため、技術スタックには Next.js + Cloudflare + PWA を採用しています。
開発したアプリの概要
Saunyは、セットごとの分数や休憩時間、さらには身体の変化(体重や水分量)など、ユーザーがこだわりたい「サ活の質」をシンプルかつ詳細に記録することを目的としたアプリです。
MVPでの主要機能:
- サ活のセットごとの詳細記録(各セットの分数、休憩の有無)
- ヘルスケア視点の記録(サ活前後の体重、水分摂取量)
- 過去のサ活履歴のタイムライン表示
- 統計データの可視化(ライブラリ非依存のカスタムSVGグラフ)
- PWAによるホーム画面からのクイック起動
現状は広告なし・完全無料でご利用できます。一部機能はログイン不要でお試しいただけます。
サウナ愛好家の方はぜひ利用してみてください。(フィードバックも大歓迎です)
高速開発を支えた基盤とデプロイ戦略
1週間という短期間でバックエンドからフロントエンドまでを完結できた背景には、過去の知見を集約した「テンプレートリポジトリ」の存在があります。
2026年1月から4月までの4ヶ月間で、私は計6つのWebサービスを個人開発し、リリースしてきました。そこで得られたCloudflare周りのベストプラクティスやモノレポ構成のノウハウをテンプレート化しており、Saunyの開発はこのテンプレートからリポジトリを初期化することから始まりました。
また、開発の進め方として「最後にデプロイする」のではなく、「初期化直後の空のリポジトリをまずデプロイする」 ところからスタートしました。その後も、機能単位で細かく本番環境へのデプロイを挟むことで、デプロイ特有のトラブルを最小化しています。
特にSNSログインの実装では、ローカル環境と本番環境を常に同期させながら動作検証を進められたことが、大幅な効率化に繋がりました。
テンプレートリポジトリの技術スタックはざっくりとこんな感じです
| カテゴリ | 選定技術 | 理由 |
|---|---|---|
| Framework | Next.js 16 (App Router) | 高速なレンダリングとSEO、Cloudflare Pagesとの抜群の相性。 |
| API / Backend | Hono | Workers/Pages Functionsの標準。RPCによる強力な型安全。 |
| ORM | Drizzle ORM | 軽量でTypeScriptの型補完が強力。Edgeランタイムに最適。 |
| Validation | Zod | APIの入出力やスキーマ定義を安全に扱える。 |
| UI Component | shadcn/ui (Tailwind CSS v4) | Tailwind CSSベースの美しく拡張性の高いUI。 |
| State Management | TanStack Query v5 | 非同期通信の管理をシンプルかつ強力に。 |
| Package Manager | pnpm (Workspaces) + Turborepo | 高速かつ効率的なモノレポ管理。 |
なぜPWAから始めたのか
初期フェーズにおいてネイティブアプリ(iOS/Android)のビルドと審査プロセスを挟むことは、検証サイクルを遅らせる要因になります。そこで、以下の理由からPWA(Progressive Web App)を先行させる戦略を採りました。
- 開発コストの削減: Next.jsの資産をそのまま活用でき、Service Workerを導入するだけでインストール可能なアプリとして振る舞える。
- 検証の高速化: 審査を介さず、Cloudflare Pagesへのデプロイ直後にユーザーが最新版を利用できる。
-
ネイティブに近い体験: Web App Manifest (
manifest.json) と Service Worker (sw.js) を活用。主要なアセットをプリキャッシュすることで、オフラインに近い環境でもスムーズな遷移を実現しました。
技術スタックとアーキテクチャ
将来的なFlutterによるネイティブアプリ化を見据え、Turborepoを用いたポリグロットモノレポ構成を採用しています。
フロントエンド
- Framework: Next.js (App Router)
- Styling: Tailwind CSS v4, shadcn/ui
- Deployment: Cloudflare Pages
Cloudflare Pagesの3MBというワーカーサイズ制限を回避するため、UIページは静的生成(SSG)を活用。Edge Workerのスクリプトサイズを300KB台まで抑制しつつ、高いパフォーマンスを実現しました。
バックエンド
- API: Hono (Cloudflare Workers)
- Database: Cloudflare D1 (SQLite)
- ORM: Drizzle ORM
- Auth: Better Auth
HonoとNext.jsの間では、Hono RPCによる型安全性を確保しています。これにより、バックエンドの変更がフロントエンドの型エラーとして即座に検知される環境を構築しました。
AI駆動開発の独自ワークフロー
基盤が整った後のスピードを加速させたのは、AIエージェントを活用した独自のワークフローです。
1. Claude Design によるプロトタイピング
まず、Claude Designを用いて、実際にブラウザで動作するHTML/JSXのプロトタイプを作成しました。これらはリポジトリ内の _prototypes/ ディレクトリに格納され、デザインの「真の仕様書」として機能します。
2. プロトタイプからコンポーネントへの変換
作成したプロトタイプをAIエージェントに読み込ませ、shadcn/uiやNext.jsのコンポーネントへ変換させます。デザインがすでにJSXレベルで固まっているため、ロジックの注入に集中でき、UIの実装時間を劇的に短縮できました。
// 仕様書(_prototypes/)のカラー定義を忠実に反映して実装する例
export const SaunaStats = () => {
const { data, isLoading } = useQuery({
queryKey: ['stats'],
queryFn: async () => {
const res = await client.api.stats.$get();
return await res.json();
}
});
if (isLoading) return <Skeleton className="h-48 w-full" />;
return (
// SAUNYカラートークン(Cream: #F7EEDC, Orange: #F5A23C)を適用
<div className="bg-[#F7EEDC] p-4 rounded-xl border-2 border-[#F5A23C]">
{/* プロトタイプの構造を維持しつつロジックを注入 */}
<CustomSVGChart data={data} />
</div>
);
};
開発中に直面した技術的な壁
AIによってコード生成は加速しましたが、インフラ特有の課題には直面しました。
SNS認証とプロキシのリダイレクト
Better Authを用いた SNS ログインの実装時、Next.jsのAPIプロキシがOAuthのリダイレクト(302)を消費してしまい、ブラウザが正しく遷移できない問題に遭遇しました。
Cloudflare Pagesの環境では、プロキシ層(Edge Runtime)で fetch がリダイレクトを自動追跡してしまうと、OAuthプロバイダーから返された302レスポンスがワーカー内で完結してしまい、クライアント(ブラウザ)にリダイレクト命令が届かないことが原因でした。
// apps/web/src/app/api/[[...path]]/route.ts
export const GET = async (req: NextRequest) => {
const url = new URL(req.url);
const targetPath = url.pathname.replace(/^\/api/, '');
// redirect: 'manual' を設定することで、302レスポンスをそのままブラウザに透過させる
const res = await fetch(`${process.env.API_BASE_URL}${targetPath}`, {
method: 'GET',
headers: req.headers,
redirect: 'manual'
});
return res;
};
このように、プロキシ層で redirect: 'manual' を設定することで、OAuthフローを正常に透過させ、Google/Xログインを本番環境で動作させることができました。
開発を終えての所感
今回の開発を通じて、AIにコンポーネント化や定型的な実装を任せることで、インフラ構築やプロキシ周りのデバッグといった、より難易度の高い課題の解決に集中できるようになったと感じています。
「コードを書く」作業の大部分を自動化し、過去のテンプレートを土台とした「デプロイファースト」の戦略を採ったからこそ、1週間という短期間で一人で完結させることができました。今後は、この基盤をベースに施設データベースとの連携や「セットごとの詳細なコンディション記録」の拡充を進め、プロダクトをより洗練させていく予定です。
個人開発者として活動中の方はぜひフォローしてください(フォローバックします!)
これまでにリリースしたWebサービスの一覧はこちら
