出発点はおよそ12万行のモノリスでした。CodeIgniter 2上のPHPバックエンド、4年後にJSONエンドポイント用に追加されたExpressの中間層、そしてjQueryとHandlebarsと誰も触りたがらないバンドルで支えられたフロントエンド。8年分のパッチ、中断された2回のリファクタ、そして2019年のどこかで途切れたドキュメント。それでもオーガニックトラフィックはまだそれなりに健全で、それが逆に最大の問題でした。一手間違えれば実際のセッションが失われ、収益も同時に落ちます。私たちが受け取ったのは、動いてはいるが触ると壊れる、典型的なレガシーでした。
Next.jsを選んだ理由は流行ではなく、具体的に三つあります。第一に、移行中にSEOを落とさないためSSRを維持したかった。第二に、チームは他のプロジェクトで既にTypeScriptを書いており、Reactのエコシステムは机上にあった選択肢の中で学習コストが最小でした。第三に、App Routerならルートを一つずつ移行でき、アプリ全体を一度に書き直す必要がない。これがクライアントの譲れない条件であり、私たちが守りきった約束でもあります。
社内ではこの戦略を「ミラールート」と呼びました。Nginxのリバースプロキシの背後、同一ドメイン上にNext.jsアプリを立ち上げ、リクエストごとに旧モノリスへ送るか新アプリへ送るかを判定するルールを設定しました。これで小さなセクション(まずブログ、次に商品一覧、最後にチェックアウト)を他に触れずに順番にリリースできました。数か月のあいだ、二つのアプリは署名付きクッキーと共通の認証エンドポイントを通じてセッションを共有しながら本番に共存していました。ユーザーには切り替えが見えず、それが目的でした。
- Screaming Frogによる監査: 47,000のURLをクロールし、正規URLごとに約180のクラスタへグループ化しました。
- 301マップ: 旧クラスタはそれぞれ単一の新URLに向け、リダイレクトチェーンを避けました。バージョン管理されたCSVと、起動時にそれを読み込むミドルウェアで運用しました。
- サイトマップの統合: 未移行の旧ルートと新ルートを統合した単一のsitemap.xmlを生成し、各リリース前にSearch Consoleへ送信しました。
画像はそれだけで別の章でした。CDNを挟まずS3バケットから直接、約400GBを配信していました。すべてをオンザフライ変換に対応したCDNへ移し、<img>タグをnext/imageに書き換えてレスポンシブサイズと明示的なアスペクト比を指定し、ついでに約12,000の孤立ファイルを削除しました。平均LCPは4.1秒から1.3秒に下がり、これだけでもユーザー体験は別物になりました。
最終切り替え後の6か月でSEOは18%上昇しましたが、魔法ではありません。初めてまともになったCore Web Vitals、モノリスには一度もなかった構造化データ(Article、Product、BreadcrumbList)、継承された不要なクエリパラメータのないクリーンなURL。Googleは当たり前のことに報いただけです。私たちは新機能ではなく、長年放置されてきた基礎を整えました。
体制は3人で4か月。フロントエンドとルート移行に2人、インフラとプロキシに1人。次に同じことをやるなら、Next.jsを一行も書く前にサイトマップと301マップから着手し、初日からビジュアル回帰テストを用意します。終盤の調整に費やした長い2週間が省けたはずで、それが今回いちばん高くついた教訓です。