𝗠𝘆 𝗔𝗣𝗜 𝗥𝗲𝘀𝗽𝗼𝗻𝗱𝗲𝗱 𝗶𝗻 4 𝗺𝘀, 𝗯𝘂𝘁 𝗡𝗮𝘃𝗶𝗴𝗮𝘁𝗶𝗼𝗻 𝗦𝘁𝗶𝗹𝗹 𝗙𝗲𝗹𝘁 𝗦𝗹𝗼𝘄

SvelteKitとRustのAPIで構築されたプロジェクト管理アプリのデバッグを行っていました。

ローカル環境では、すべてが瞬時に動作しているように感じられました。しかし、VPS上ではナビゲーションが重く感じられました。「Tickets」や「Timeline」といったページを開くのに時間がかかりすぎ、チケットをクリックしてからプレビューが表示されるまでに、はっきりとした遅延が発生していました。

問題はサーバーにあると考えていました。データベースのクエリが遅いのか、あるいはVPSのスペックが低いのではないかと疑っていました。

しかし、計測結果は私の予想を裏切りました。

feature listのエンドポイントを確認しました。わずか52件のチケットに対して、APIのレスポンスは4msでした。一見すると非常に高速です。しかし、レスポンスサイズは354 KBもありました。

SvelteKitのルートペイロードも同様の問題を示していました。単純なリストにしては、データサイズが巨大すぎたのです。

問題は速度ではなく、重さ(データ量)でした。

説明文だけでレスポンス全体の80%を占めていました。リストのエンドポイントが、全アイテムに対して完全なMarkdown形式の説明文を返していたのです。

これはよくある罠です。リストのエンドポイントが、いつの間にか詳細(detail)エンドポイントになってしまっていました。ページがリストを必要とするたびに、使用しないデータに対してもコストを支払っていたのです。

UIの使いかたがペイロードを決定するのではありません。loaderの戻り値の形状(shape)が決定するのです。

loaderが完全なオブジェクトを返すと、SvelteKitはそのオブジェクト全体をルートデータとしてシリアライズします。たとえそのフィールドをレンダリングしなくても、データはネットワーク上を流れてしまいます。

私は、サマリーデータと詳細データを分離することでこれを解決しました。

2つの異なるコントラクト(規約)を作成しました:

• サマリー用のリスト・コントラクト(ID、タイトル、ステータス、日付)。 • 完全な情報用の詳細・コントラクト(説明文、コマンド)。

APIを2つのエンドポイントに分割しました:

  • GET /features (サマリーを返す)
  • GET /features/:id (1件の詳細を返す)

フロントエンドの動作も変更しました。現在は、サマリーデータを使用してリストを即座にレンダリングします。チケットをクリックすると、アプリはスケルトン(shell)を表示し、バックグラウンドで重い詳細データを取得します。

結果は劇的でした:

• Feature list API: 89.6% 削減 • Tickets route data: 91.4% 削減 • OpenSpec docs API: 98.9% 削減

データベースは常に高速でした。真のボトルネックは、APIとページ間のデータコントラクトにありました。

リストを多用するアプリへの教訓:

  • レスポンス時間だけでなく、レスポンスサイズも測定する。
  • リスト用と詳細用のペイロードは別物として扱う。
  • リストビューで大きなテキストフィールドを返さない。
  • SSRローダーが実際に何をシリアライズしているかを確認する。
  • 詳細データは遅延読み込み(Lazy-load)し、UIシェルは即座に表示する。
  • 重いペイロードを隠すためにローディングスピナーを使用しない。

クエリが速いからといって、ページが速いとは限りません。

出典: https://dev.to/ahikmah/my-api-responded-in-4-ms-but-navigation-still-felt-slow-1hk8