こんにちは、Karouです。Egg Masterは最初、単一のHTMLファイルとして誕生しました。そこから機能が増えるにつれてNext.js + TypeScriptへ移行した過程と、そこで得た知見を共有します。
なぜ移行が必要になったのか
最初のEgg Masterは1つのHTMLファイルにHTML・CSS・JavaScriptをすべて詰め込んだ構成でした。プロトタイプとしては十分でしたが、以下の問題が顕在化してきました。
- コードの肥大化:物理演算ロジック、UI、設定管理がすべて1ファイルに混在し、見通しが悪化。
- ゆで卵モード追加の壁:目玉焼きとゆで卵で別ページが必要になり、単一ファイルでは限界。
- 型安全性の欠如:複雑な物理演算パラメータに型定義がなく、バグの温床に。
- コンポーネント再利用:コントロールパネルやレポート画面など、共通UIの重複が増加。
移行後のアーキテクチャ
Next.js App Router構成に移行し、全ロジックを型付きモジュールに分割しました。
ディレクトリ構成
├── 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の組み合わせは、無料で高速な配信ができる強力な選択肢です。同様の構成を検討している方の参考になれば幸いです。