開発者として、誰かがあなたのWebサイトの読み込みが遅いと言ったとき、あなたはどんな反応をすべきでしょうか?
「ユーザーに任せている」と言わない限り、あなたはすでに正しい道を歩んでいます。ユーザーの苦痛を和らげることを選択したのですから、読み込みの遅さやパフォーマンスの問題を特定し、修正するプロセスをご案内します。
実際、パフォーマンスの問題をどのように解決するかはさまざまですが、開発者としては、解決への最短経路を常に求めています。
小規模から中規模の静的サイトであれば、ブラウザの開発ツールに組み込まれているLighthouseスコアで十分でしょう。個々のページのCore Web Vitalsが表示され、通常、Webサイトのパフォーマンスを改善するために必要な変更を行うのに十分な情報が得られます。とはいえ、Lighthouseのスコアだけを目標にすべきではありません。
Webサイトが成長したり、静的でなくなったりすると、パフォーマンスの問題をデバッグするために、より詳細な洞察が必要になります。
この投稿では、私が開発したサイトを紹介します。このサイトは静的Webサイトとは程遠く、各ルートは人気リズムゲームのゲーム内スコアに基づいて、ユーザーにカスタムされたポケモンのようなカードを動的にレンダリングします。
そのような動的な性質をもつため、すべてのブラウザとユーザーが使用する可能性のある・すべてのネットワークで・すべてのルートをテストすることは不可能です。
その代わりに、実際のユーザーによる実際のセッションのパフォーマンスをモニターして、いつ問題が発生し、どのように解決できるかを知る必要があります。
問題の発見
モニタリングツールがなければ、ドッグフーディング(アプリを自分で使用すること)、またはユーザーレポートという2つの方法のいずれかでパフォーマンスの問題に遭遇する可能性が高いです。
ドッグフーディングは重要ですが、インターネット接続が良好なハイエンドのデバイス1台でしかアプリを使用していない可能性が高く、それがユーザーの間で一般的かどうかを検討する必要があります。ユーザーレポートは非常に有用ですが、正直なところ、明らかなレポートチャネルを持っていない場合、それらはほとんどありません(そして、その場合でも、役に立たないか、または再現するのが難しい場合があります)。
常にログを読み、リクエストのタイミングを計っていない限り、どこで間違ったのか、なぜ間違ったのかを正確に突き止めることは難しいのです。
Sentryのユーザーフィードバックとセッションリプレイ
Sentryでは、あなたのサイトに常に存在するユーザーフィードバックウィジェットにオプトインすることができます。
このウィジェットは、ユーザーが見つけたバグを直接Sentryのダッシュボードに報告することができ、スクリーンショットを追加するオプションもあるので、即座に実用的な洞察を得ることができます。
さらに、Sentryは、このフィードバックを「セッションリプレイイベント」と連動させ、ユーザーがボタンをクリックしたり、サイト内を移動するたびに、追加の有用な詳細を含むパンくずなど、ユーザーのWebサイトとのインタラクションの完全な再現を見ることができます。
最近、Sentryのウィジェットを介してユーザーがフィードバックを送信し、ロードに時間がかかる理由を尋ねたとき、私はWebサイトのページロードが遅いことに気づきました。セッションのリプレイをチェックすると、明らかに読み込みが非常に遅かったのですが、フィードバックとセッションのリプレイで提供された情報でも、大きな画像や過剰なJavaScriptのような明らかな問題や迅速な解決策を見つけることができませんでした。この問題については、Sentryのトレースビューをもう少し深く掘り下げる必要がありました。
トレースの使用
Sentryのトレース機能は、フロントエンドからバックエンド、そしてサービス間のトランザクションをリンクし、ソフトウェアの接続されたビューを提供することで、コードのパフォーマンスを追跡するために使用されるツールです。
トレースビューは、各トランザクションにかかる時間から、より具体的な問題のデバッグを助けるためにユーザが使用したデバイスやブラウザまで、あらゆる情報を提供します。トレースビューは、ユーザーフィードバックや問題によってトリガーされたリプレイにも添付され、ユーザーがページをロードしたとき、ボタンをクリックしたとき、サイト内を移動したときなど、舞台裏で起こったすべてのことを簡単に調べることができます。
セッションリプレイイベントのトレースを参照した後(パフォーマンスタブを少し調べると同時に)、私のサイトの(かなりひどい)スローダウンはナビゲーション中のロード関数にあることがわかりました。それはいいことですが、私は15分以上前にコードを書いたのです。幸運なことに、Sentryはリクエストまでの詳細をすぐに提供してくれました。
修正
リピート・リクエスト
私が犯した最初のミスは、それを書くときに明らかであったはずなのですが、不必要に同じリクエストを何度も繰り返し、一度に一つのものだけを取り出すことでした。ユーザー」オブジェクトを構築する際に、大きなJSONデータの塊から特定のプロパティを取り出す必要があったのですが、1回の関数呼び出しで必要なプロパティを返すのではなく、同じ関数を8回繰り返し呼び出して配列を作成し、その都度1つのプロパティしか取得しませんでした。
Sentryのトレースビューで、私はすぐに問題を特定することができました。
青い 「自動グループ化された 」スパン(2秒近くかかりました🤮)が最初に目に留まり、それを拡大して各スパンが同じリクエストエンドポイントを持っているのを見ると、私のコードで何が修正されるべきかは明白でした。
リクエストウォーターフォール
さてさて、滑稽なほど酷いコードはこのくらいにして、解決策があまり明確でない問題に移りましょう。上の画像では、「自動グループ化された 」リクエストの先にも、ページロードの終わりまで小さな階段があるのがお分かりいただけるでしょう。これはリクエストウォーターフォールと呼ばれるもので、リクエスト(この場合は外部APIへのリクエスト)を直列に行い、前のリクエストを終えてから次のリクエストを開始することで発生するものです。
このウォーターフォールはできるだけ避けたいものですが、リクエストを連続して行う必要がある状況もあります(一般的には、ある呼び出しのリクエストが前の呼び出しの結果に依存している場合)。
1回の呼び出しで楽曲IDのリストを取得しているのですが、より具体的な情報を取得するには、特定のIDごとに個別のリクエストを行う必要があります。私が最初に考えたのは、1回のリクエストですべての情報を集めることはできないので、1つずつ呼び出す必要があり、現在持っているパフォーマンスはそのままになってしまうということでした。幸いなことに、この問題を抱えたのは私が初めてではなく、リクエストのウォーターフォールを回避する2つの解決策を見つけました。
直列運転と並列運転
ロード時間のパフォーマンスを最適化する場合、リクエストを直列に実行しないことが最も重要ですが、常に可能というわけではありません。直列に実行する必要があるときと、そうでないときを見分けることが重要です。
なぜなら、可能な限りリクエストを並行して実行する必要があるからです。
直列実行の反対は並列実行で、その名の通り、すべてのリクエストが同時に実行され、最も遅いリクエストが終了するたびにグループとして解決されます。JavaScriptでは、コード上の違いはごくわずかですが、実行時に実際に起こることは大きく異なります。
上記のように、リクエストを直列に呼び出すコードから並列に呼び出すコードに変更した後、最終的に恐ろしいウォーターフォールを取り除くことができ、Sentryでこれを確認することができます。両方のロード関数のトレースを比較すると、かつては階段状だったものが、今では崖のようになっており、最も遅いリクエストが終了するとすぐにページがロードされることがわかります。リクエストの処理方法を変更した後、P75(75%のユーザーにとっての平均ページ読み込み速度)を6.5秒からわずか2.3秒まで下げることができました。
一歩前進
SvelteKit(またはReactではこのようなもの)のpromiseストリーミングを使えば、このパフォーマンスの大幅な向上をさらに一歩進めることが可能になります。
サーバーのロード関数からクライアントにpromiseをストリーミングすることで、すべてのリクエストが終了する前にページをレンダリングできるようになります。これには累積レイアウトシフト(Cumulative Layout Shift)の可能性に対処する複雑さが加わりますが、ページのロード速度を劇的に改善する機会があり、ユーザーにとって最も重要なデータをより迅速に利用できるようになります。
パフォーマンスには価値がある
この記事の冒頭で、最も早く解決する道は常に取るべき道であると述べました。
パフォーマンスの問題は、「バグ 」ではないという点でユニークであり、Webサイトのエラーやクラッシュを引き起こしていないため、多くの開発者が見過ごしてしまうことがあります。しかし、一貫して速い読み込み速度は、10,000セッションに1回起こるエッジケースエラーよりも、ユーザーにとって同じように、いやそれ以上に重要です。Sentryを使用することで、このような抽象的な問題の発見と解決が非常に簡単になります。
パフォーマンス問題のデバッグの詳細
Sentryのトレースがパフォーマンス問題のデバッグにどのように役立つのか、もっと知りたいですか?
悪いLCPスコアをバックエンドの問題にトレースするSalmaのブログ記事をチェックするか、以下のワークショップの全容をぜひご覧ください。