レスポンスの遅延を80%削減した方法
Subitoはイタリアのトップマーケットプレイスです。私たちは毎分数千件のリクエストを処理しています。広告詳細ページは、SEOと収益にとって極めて重要です。
このページをNext.jsのPages RouterからApp Routerへ移行しました。移行のためにプロダクト開発を止めることはせず、小さなステップを踏んで進めました。
その手法を以下に紹介します。
アプローチ
私たちはインクリメンタル(段階的)な戦略を採用しました。従来のPages Routerと並行してApp Routerのツリーを追加しました。これにより、移行を進めながら新しい機能をリリースすることができました。
データフェッチを管理するためにServer Componentsを使用しました。これにより、多くのレイヤーを経由してデータを渡す必要がなくなりました。また、Data Access Layerも作成しました。これにより、1回のリクエストにつきデータの取得が1回のみであることを保証しました。
HTMLストリーミングを有効にするために、Suspenseとuse()フックを使用しました。これにより、データ読み込み中にスケルトンスクリーンが表示されるようになり、ユーザーはより早くページを確認できるようになりました。
課題
問題1: HTTP 410 Gone 検索エンジンは、ページが恒久的に削除された際に410ステータスを必要とします。App Routerには、これを送信する組み込みの方法がありません。私たちはExpressサーバーを使用してこれを解決しました。サーバーがレスポンスをインターセプトし、ステータスコードを変更するようにしました。
問題2: HTMLストリーミング ローディング用のスケルトンが表示されないことが分かりました。ページが数秒間空白のままになってしまったのです。
NginxとAkamaiがレスポンスをバッファリングしていることが判明しました。CDNがHTMLをバッファリングしてしまうと、ストリーミングは失敗します。Nginxのバッファリングをオフにし、Akamaiにカスタム設定を適用する必要がありました。これを修正したことで、コンテンツはユーザーに完璧にストリーミングされるようになりました。
ロールアウト
SEOを守るために、2つのフェーズに分けてロールアウトしました。
フェーズ1: トラフィックをApp Routerに移行しましたが、ストリーミングはオフのままにしました。これをカテゴリーごとに一つずつ実施しました。
フェーズ2: HTMLストリーミングを有効にしました。まずは小規模なカテゴリーでテストを行いました。次のカテゴリーへ移行する前に、SEOチームが2週間にわたってランキングをモニタリングしました。
結果
結果は絶大でした。移行前は、レスポンスの最大40%が低速でした。移行後、低速なレスポンスは80%減少しました。
現在、ほとんどのページが500ms未満でロードされます。
教訓
- 機能開発を止めることなく移行するために、parallel routingを活用しましょう。
- カスタムサーバーレイヤーを使用すると、トラフィックの制御やフレームワークの欠落を補うのに役立ちます。
- ストリーミングには、すべてのレイヤーでの設定が必要です。NginxやCDNの設定を確認してください。
- ロールアウト中はSEO指標をテストしてください。
Next.js App Routerへの移行により、低速なレスポンスを80%削減した方法
高性能なeコマースプラットフォームを構築する道のりにおいて、私たちは大きなボトルネックに直面しました。それは、レスポンスの遅延です。
具体的には、TTFB(Time to First Byte)が高く、ユーザーは遅延を経験していました。
Pages Routerは強力なフレームワークですが、いくつかの制限がありました。主な問題は以下の通りです:
- 巨大なJavaScriptバンドル: ページをレンダリングするために、クライアント側で大量のJavaScriptを実行する必要がありました。
- データフェッチのウォーターフォール: コンポーネントがネストされているため、親コンポーネントのデータ取得が終わるまで子コンポーネントのデータ取得が開始できず、リクエストが連鎖的に発生していました。
- きめ細かなローディング状態の欠如: ページ全体が準備できるまでユーザーは何も見ることができず、体感的なパフォーマンスが低下していました。
そこで、私たちはNext.js App Routerへの移行を決定しました。
なぜApp Routerが効果的だったのか
1. React Server Components (RSC)
RSCを活用することで、データフェッチの大部分をサーバー側で行うことができました。これにより、クライアントに送信されるJavaScriptの量を劇的に減らすことができ、Hydrationのコストも大幅に軽減されました。
2. Streaming と Suspense
Suspense を使用して、ページ全体が読み込まれるのを待つのではなく、準備ができたコンポーネントから順次ストリーミングして表示を開始できるようになりました。これにより、ユーザーはページの主要な部分をより早く目にすることができ、体感的な待ち時間が大幅に短縮されました。
3. データフェッチの改善
App Routerの拡張されたfetch APIにより、サーバー側でのキャッシュとリクエストの重複排除(deduplication)が容易になりました。これにより、同じデータを複数のコンポーネントで必要としても、ネットワークリクエストは一度だけで済みます。
結果
この移行により、パフォーマンスは劇的に改善されました。
- 低速なレスポンスの割合: 80%削減
- TTFB: 大幅な改善
- LCP (Largest Contentful Paint): 改善
Next.js App Routerへの移行は、単なる技術的なアップグレードではなく、ユーザー体験を根本から改善するための戦略的な決定でした。