こんにちは、Karouです。Egg Masterは最初、単一のHTMLファイルとして誕生しました。そこから機能が増えるにつれてNext.js + TypeScriptへ移行した過程と、そこで得た知見を共有します。

なぜ移行が必要になったのか

最初のEgg Masterは1つのHTMLファイルにHTML・CSS・JavaScriptをすべて詰め込んだ構成でした。プロトタイプとしては十分でしたが、以下の問題が顕在化してきました。

  • コードの肥大化:物理演算ロジック、UI、設定管理がすべて1ファイルに混在し、見通しが悪化。
  • ゆで卵モード追加の壁:目玉焼きとゆで卵で別ページが必要になり、単一ファイルでは限界。
  • 型安全性の欠如:複雑な物理演算パラメータに型定義がなく、バグの温床に。
  • コンポーネント再利用:コントロールパネルやレポート画面など、共通UIの重複が増加。

移行後のアーキテクチャ

Next.js App Router構成に移行し、全ロジックを型付きモジュールに分割しました。

ディレクトリ構成

egg-master/
├── app/     # App Router (ページ)
├── components/     # UI コンポーネント
│ ├── ControlsPanel
│ ├── SimulationView
│ ├── ReportPanel
│ ├── CookingGuide
│ └── SettingsPanel
├── lib/     # ロジック・定数
│ ├── constants.ts
│ ├── simulation.ts
│ ├── report.ts
│ ├── eggCode.ts
│ ├── presets.ts
│ └── guide.ts
└── public/     # 静的アセット

移行で得た3つの学び

1. 静的エクスポートとFirebase Hostingの相性

Firebase Hostingで配信するため、Next.jsのoutput: 'export'を使用して静的ファイルを生成しています。ここで注意点がありました。

当初、Firebase Hostingのrewrites設定を使っていましたが、/boiled(ゆで卵ページ)が正しく配信されない問題が発生。cleanUrlsに変更することで解決しました。また、router.pushも静的エクスポート環境では動作しないケースがあり、window.location.hrefへの書き換えが必要でした。

2. TypeScriptによる物理演算の安全性

移行前は「温度」「時間」「凝固度」などの数値が型なしで飛び交い、意図しない計算ミスの原因になっていました。TypeScript導入後は、パラメータの型を厳密に定義することで、「フライパンの熱容量に卵の鮮度を代入してしまう」ような凡ミスをコンパイル時に検出できるようになりました。

3. コンポーネント分割がもたらした開発速度

目玉焼きシミュレーターで作ったControlsPanelやReportPanelを、ゆで卵シミュレーターでも再利用できたのは大きな恩恵でした。ゆで卵モードの追加は、既存コンポーネントの組み合わせ+物理演算ロジックの差し替えで済み、想定よりも短期間で実装が完了しました。

ハマったポイント:タイマー多重起動バグ

移行後に最も厄介だったバグは、シミュレーション画面を行き来するとタイマーが多重起動する問題でした。handleStart内でsetIntervalを開始する前に、前回のタイマーをクリアしていなかったことが原因です。

画面遷移を繰り返すたびにタイマーが増殖し、体感時間が倍速で進むという奇妙な現象に。showTransition呼び出し前とコールバック内の2箇所でclearIntervalを実行することで解決しました。SPAならではの罠ですね。

まとめ

「最初は小さく、育ったら移行する」という個人開発の王道パターンを、Egg Masterでまさに体験しました。HTMLプロトタイプで素早く動くものを作り、必要になった段階でフレームワークに移行する。この段階的アプローチは、個人開発者にとって非常に合理的だと感じています。

Next.jsの静的エクスポート + Firebase Hostingの組み合わせは、無料で高速な配信ができる強力な選択肢です。同様の構成を検討している方の参考になれば幸いです。

← 前の記事:Egg Masterを作った話 一覧 次の記事:調理支援モードの開発裏話 →