はじめに
個人開発している「近況ノート」を、React RouterのSPAからNext.js へ移行しました。
近況ノートは、同窓会や久しぶりの集まりを前に、同級生同士で現在地や仕事、最近の出来事を共有するためのWebアプリです。
共有URLを知っている人はログインせずに近況を閲覧でき、自分の近況を投稿・編集するときはLINE Loginを利用します。投稿へのいいねやコメントなど、簡単な交流機能もあります。
今回の移行を始めた一番の理由は、SDD(Spec-Driven Development/仕様駆動開発)を練習してみたかったからです。
すでに動いているアプリを題材にして、AIへいきなり実装を依頼するのではなく、先にプロジェクトの前提、要件、設計、実装タスクを文章にしてから移行を進めました。
この記事は全3編でまとめます。
- 前編:steering documentsと移行仕様を作るまで
- 中編:作成したタスクに沿ってNext.jsへ移行する過程
- 後編:本番切り替え、旧構成の削除、SDDを試して分かったこと
前編では、実装を始める前に何をAIへ伝え、どのような文書を作ったのかを紹介します。
SDDの題材にNext.js移行を選んだ
今回は、SDDを導入して開発上の問題を解決しようとしたというより、まず一度SDDの流れを最後まで試してみることが目的でした。
題材には、近況ノートのNext.js移行を選びました。
新規アプリの開発では、途中で仕様そのものを変えたくなることがあります。一方、既存アプリの移行では、移行前の画面や操作が比較対象になります。
近況ノートにも、移行で壊したくないものがすでにありました。
- グループごとのURL
- 近況の閲覧、投稿、編集
- LINE Loginとログイン後の投稿再開
- Honoで作った既存API
- Cloudflare D1に保存しているデータ
- いいね、コメント、アバター画像
- モバイル向けの画面と操作感
「何を変えるか」と「何を変えないか」がはっきりしているため、要件と受入条件を書く練習に向いていると考えました。
最初にsteering documentsを作る
本格的な設計と実装に入る前に、既存リポジトリを調べてもらい、プロジェクト全体の前提となるsteering documentsを作りました。
作成したのは、次の3ファイルです。
.spec-workflow/steering/
product.md
tech.md
structure.md
AIへ伝えた内容を要約すると、次のようになります。
既存の近況ノートを調査し、プロダクトの目的、採用技術、プロジェクト構造をsteering documentsとして整理して。
実際には移行要件のたたき台も並行して作っていましたが、その後の設計と実装で判断基準として使えるように、先にこの3文書を整えました。
product.md
product.mdには、近況ノートが誰のための、どのようなアプリなのかをまとめました。
主な内容は次のとおりです。
- 同級生や小規模な仲間同士で近況を共有する
- 幹事が一人ずつ近況を集める手間を減らす
- 閲覧は気軽にし、投稿や編集では本人性を守る
- ログインの往復があっても入力内容を失わせない
- 不特定多数向けSNSではなく、小さなコミュニティの安心感を優先する
この文書によって、技術移行でも守るべき利用体験が明文化されました。
Next.jsへ移行できても、ログイン後に入力途中の近況が消えてしまえば、近況ノートとしては移行成功とはいえません。
tech.md
tech.mdには、現在の技術構成と移行後も維持する境界をまとめました。
移行前の主な構成は次のとおりです。
React + Vite + React Router
-> Hono API on Cloudflare Workers
-> Drizzle ORM
-> Cloudflare D1
このほか、LINE Login、セッション、CSRF、Zod、React Hook Form、Tailwind CSSなども記録しました。
Next.jsへ移行しても、既存の/api/*、D1スキーマ、LINE OAuth、Cookieの振る舞いは安定した境界として扱う方針にしました。
structure.md
structure.mdには、コードをどこへ置き、どの方向へ依存させるかをまとめました。
近況ノートでは、移行前から次のような責務分離を採用していました。
presentation
-> application
-> infrastructure
-> shared
Next.js移行後は、ここへApp Routerのappディレクトリが加わります。
ルートファイルへ業務処理を集めないこと、ブラウザAPIが必要な部分だけをClient Componentにすること、既存のユースケースやAPIクライアントを再利用することなどを決めました。
Next.js移行の仕様を作る
steering documentsを判断基準として使える状態にしたあと、Next.js移行のspecを作りました。
AIへ伝えた移行内容を要約すると、次のようになります。
Vite + React Routerで作られた近況ノートをNext.js App Routerへ移行する。Cloudflare WorkersへのデプロイにはOpenNextを使い、既存のHono API、D1、LINE Login、URL、画面、主要な操作は維持する。
設計では、Next.jsとCloudflareの公式資料に加えて、akfm_satoさんの『Next.jsの考え方』を参考にすることも伝えました。
この指示から、次の3文書を作成しました。
.spec-workflow/specs/nextjs-migration/
requirements.md
design.md
tasks.md
それぞれを作って終わりにせず、内容を確認して承認してから次の段階へ進めました。
requirements.mdで移行の成功条件を決める
最初に作ったのが要件定義書です。
要件は、次の7項目に分かれました。
- 既存の振る舞いを維持したNext.js移行
- API互換性とバックエンドの継続性
- Cloudflareに適合するデプロイ構成
- フロントエンド構成とルーティングの明確化
- Server Components中心のデータ取得と状態遷移
- セキュリティとプライバシーの維持
- 検証可能性とロールバックの安全性
各要件には受入条件も作りました。
たとえば「既存の振る舞いを維持する」だけでは、どこまで確認すればよいか分かりません。
そこで、既存URLをNext.jsで表示できること、存在しないグループはNot Foundにすること、LINE OAuthやCSRFを維持すること、移行中はD1スキーマを変更しないことなどを具体的な条件にしました。
この段階で、単に「Next.jsで画面が表示されたら完了」ではないことが明確になりました。
design.mdで移行後の構成を決める
要件の次に、設計書を作りました。
設計で大きな判断になったのは、Next.jsと既存のHono APIをどう組み合わせるかです。
最終的に、次の2 Worker構成を採用しました。
ブラウザ
-> Next.js / OpenNext 公開Worker
-> Service Binding
-> Hono API Worker
-> Cloudflare D1
ブラウザからは、これまでと同じ同一オリジンの/api/*へアクセスします。
公開WorkerはAPIリクエストをService Binding経由でHono API Workerへ転送します。D1やLINE LoginのシークレットはHono側だけに持たせます。
画面については、初期データをServer Componentで取得し、フォーム、下書き保存、画像処理、いいね、コメント、共有操作など、ブラウザでの操作が必要な部分だけをClient Componentとして残す設計にしました。
設計書には、このほかにも次の内容が含まれています。
- App RouterへのURL対応
- Server ComponentとClient Componentの境界
- API GatewayとCookieの転送
- キャッシュとRequest Memoization
- CSP、CSRF、認証、認可
- テスト方法
- 本番デプロイとロールバック
ここまで決めてから、実装単位への分解に進みました。
tasks.mdで21個の実装タスクへ分解する
要件と設計をもとに、移行作業は21個のタスクに分かれました。
大きく分けると、次の流れです。
移行用の依存関係とテスト基盤を追加
-> Next.js WorkerとHono API Workerを分離
-> App Routerの土台を作成
-> 各画面をServer/Client Componentへ分割
-> React Router依存を除去
-> 自動テストとOpenNextプレビューで検証
-> 本番へ段階的にデプロイ
-> 安定確認後にVite版を削除
各タスクには、作業内容だけでなく、次の項目も含まれています。
- 変更対象のファイル
- タスクの目的
- 再利用する既存コード
- 対応する要件
- やってはいけないこと
- 完了と判断する条件
たとえば、最初のタスクはNext.jsとOpenNextの依存関係を追加する作業です。
ただし、同時にVite版を削除することは禁止しました。まず両方を動かせる状態にし、Next.js版の検証が終わるまでVite版をロールバック手段として残すためです。
また、本番環境を変更するタスクには、実行直前に利用者の明示的な許可を得る条件も入りました。
単なるTODOリストではなく、要件と設計を実装時にも守るための指示書になっています。
前編のまとめ
今回は、SDDの練習として近況ノートのNext.js移行に取り組みました。
実装前に作ったものは、次の6文書です。
steering documents
product.md
tech.md
structure.md
Next.js移行spec
requirements.md
design.md
tasks.md
既存アプリを調査してプロジェクトの前提を整理し、移行要件、設計、21個の実装タスクへ順番に具体化しました。
ここまでの段階では、まだNext.jsの画面はほとんど作っていません。
それでも、守るべき機能、採用する構成、検証方法、ロールバック方法まで文章になっているため、実装中に判断がぶれにくい状態を作れました。
中編では、作成したタスクに沿って、Vite版を残したままNext.jsとOpenNextの構成を作り、各画面をServer ComponentとClient Componentへ分割していった過程をまとめます。