本文へ移動

ホーム / Web / 第3部 フロントエンド

第3部 フロントエンド・第7章

Web パフォーマンス

★★☆ 重要10トピック・面接 4問

表示の速さと操作の軽さを、指標で測って改善する方法を押さえる。

ひとことで

推測で直さず、範囲の確認 → 計測 → どこが遅いかの切り分け → 1つずつ直す → 効果の確認 → 劣化を防ぐ、の順で進める。

「遅い」と言われたら:計測してから、1つずつ直して確かめる1. 範囲と再現どの画面・誰が・いつから端末・回線・ログインの有無2. 計測RUM の p75・ラボ計測どの指標が悪いか3. どこが遅いかフロント/通信/サーバーウォーターフォールで分ける4. 仮説を立てて直す効きそうな順に1回に1つだけ変える5. 効果を確かめる同じ条件で前後を比べるRUM で本番の数字も6. 劣化を防ぐバジェット・CI・アラート原因と対策を記録効果がなければ次の仮説へ推測で直さない。「計測 → 1つ直す → 計測」を回し、直した効果を数字で残す
Web の性能改善の流れ

「サイトが遅い」と言われたら、いきなりコードを直さず、どこが・どれだけ遅いかを計測してから直します。思い込みで直すと、効かない作業に時間を使ったり、別のところを遅くしたりします。

段階やること道具の例
1. 範囲と再現どの画面か・全員か一部か・いつからか(リリースや流入の変化)。遅い人の端末・回線・地域問い合わせの内容、リリース履歴
2. 計測実際の利用者の数字(RUM)で、どの指標(LCP・INP・CLS・TTFB)が悪いかを見る。平均ではなく p75・p95RUM(web-vitals ライブラリ・APM)、Search Console
3. どこが遅いかフロント(JS・画像・描画)/通信(CDN・リダイレクト)/サーバー(API・DB)のどれかに分ける開発者ツールの Network・Performance、Lighthouse、APM のトレース
4. 仮説を立てて直す効果が大きく手間が小さい順に。1回に1つだけ変えて、何が効いたか分かるようにする下の各トピックの手段
5. 効果を確かめる同じ条件(端末・回線の制限)で前後を比べ、リリース後は RUM で本番の数字を確かめるLighthouse を複数回、RUM のグラフ
6. 劣化を防ぐバンドルサイズや指標に上限(バジェット)を決め、CI とアラートで戻らないようにするLighthouse CI、size-limit、RUM のアラート

報告するときは「LCP の p75 が 4.1 秒 → 2.3 秒(原因:トップの画像が 1.2MB で優先度が低かった)」のように、指標・前後の数字・原因をセットにします。性能のテストの進め方は QA の性能のテスト、サーバー側の深掘りは SRE のパフォーマンスチューニングも参考になります。

面接ではこう答える

性能の問題は、推測で直さずに計測から始めます。まずどの画面がいつから遅いかを確かめ、実際の利用者の数字でどの指標が悪いかを見ます。次にフロント・通信・サーバーのどこが遅いかを分け、効果が大きい順に1つずつ直して、同じ条件で前後を比べます。最後にバジェットや CI で劣化を防ぎ、指標と前後の数字と原因をセットで記録します。

7.2Core Web Vitals

用語★★☆ 重要
ひとことで

LCP(表示の速さ)・INP(操作への反応)・CLS(レイアウトのずれ)の3つの指標。

Core Web Vitals は、Google が決めたユーザー体験の指標です。検索順位の要素の1つにもなっています。操作への反応の指標は、2024年3月に FID から INP に置き換わりました。目安は、実ユーザーの 75 パーセンタイル(遅いほうから4分の1の人)で判定します。

3つの指標の「良い・要改善・悪い」の区切りLCP表示の速さ良い要改善悪い2.5 秒4 秒INP操作への反応良い要改善悪い200ms500msCLSレイアウトのずれ良い要改善悪い0.10.25判定は実ユーザーの 75 パーセンタイル(4 人のうち 3 人が「良い」に入っているか)
Core Web Vitals の3つの指標の区切り(長さは目安)
指標測るもの良い悪くなる原因
LCP(Largest Contentful Paint)一番大きな画像や文章が出るまで2.5 秒以内遅い TTFB、大きなヒーロー画像、描画を止める CSS・JS、CSR で JS の後にしか出ない
INP(Interaction to Next Paint)クリックなどから画面が変わるまで200ms 以内メインスレッドを長く占める JS、重いイベント処理、大量の再描画
CLS(Cumulative Layout Shift)表示中のレイアウトのずれ0.1 以下サイズ指定のない画像、後から差し込まれる広告・バナー、Web フォントの切り替え

面接ではこう答える

Core Web Vitals は、Google が決めたユーザー体験の3つの指標です。LCP は一番大きな要素が出るまでの時間で 2.5 秒以内、INP は操作してから画面が変わるまでの時間で 200 ミリ秒以内、CLS はレイアウトのずれで 0.1 以下が良いとされます。判定は実ユーザーの75パーセンタイルで行い、検索順位の要素の1つにもなっています。

ひとことで

RUM は本番の実ユーザーのブラウザから集めた値、ラボは決まった条件で測った値。目標は RUM、原因の調査はラボで。

目標は実ユーザーで決め、原因はラボで探すラボ(Lighthouse など)・決まった端末・回線で測る・前後の比較・原因調査・INP は測れず TBT で代用RUM(実ユーザー)・本番のブラウザから集める・遅い端末・回線も入る・web-vitals・CrUX
ラボの計測と実ユーザーの計測
  • ラボ:Lighthouse や開発者ツールで、決まった端末・回線の条件で測る。毎回同じ条件なので、変更の前後の比較や原因の調査に向く。INP は操作がないと測れないので、代わりに TBT(メインスレッドが止まった時間)を見る。
  • RUM(実ユーザー計測):本番で実際のユーザーのブラウザから値を集める。web-vitals ライブラリで測って送る、または Chrome の実データ(CrUX、Search Console・PageSpeed Insights に出る)を見る。遅い端末や回線のユーザーも入るので、本当の体験が分かる。

ラボでは速いのに RUM では悪い、はよくあります。目標は RUM で決め、原因の調査はラボで行います。

RUM の値はページの種類・端末(スマホ/PC)・国で分けて見ます。全体の平均だと、遅いページが隠れます。

面接ではこう答える

性能の計測には2種類あります。Lighthouse などのラボの計測は、決まった端末と回線で測るので、変更の前後の比較や原因の調査に向きます。RUM は本番のユーザーのブラウザから値を集めるので、遅い端末や回線のユーザーも含めた本当の体験が分かります。Core Web Vitals の合否は実ユーザーの75パーセンタイルで判定されるので、目標は RUM で決め、原因はラボで探します。

ひとことで

悪い指標から原因と手段を選び、1つ直しては測る。LCP・INP・CLS で疑う所が違う。

測る → 一番悪い所 → 1つ直す → また測るRUM で悪い指標LCP・INP・CLS悪いページを特定種類・端末別ラボで原因Performance パネル1つ直して測る前後を比べる
性能を改善する進め方

まず「どの指標が悪いか」で手段を選びます。

悪い指標まず疑うもの主な手段
LCPTTFB・大きな画像・描画を止める CSS と JSCDN のキャッシュ、画像の形式とサイズ、fetchpriority、JS の削減
INPメインスレッドを長く占める JSコード分割、長い処理の分割、サードパーティの見直し
CLSサイズのない画像・後から入る要素・フォントwidth・height、場所の確保、font-display と代わりのフォントの調整

面接ではこう答える

性能の改善は、まず実ユーザーの計測でどの指標が悪いかを確かめ、悪いページを特定し、ラボで原因を見つけてから、1つずつ直して測る、の順で進めます。LCP なら TTFB と大きな画像、INP ならメインスレッドを長く占める JS、CLS ならサイズのない画像や後から入る要素をまず疑います。推測で直すと、効果がない変更に時間を使ってしまいます。

7.5画像の最適化

★★☆ 重要
同じ写真でも、形式と大きさで重さが大きく変わる4000px の JPEG を縮小表示重い表示サイズの JPEG表示サイズの WebP表示サイズの AVIF一番軽いことが多い
画像の重さのイメージ(相対値。実際の比率は画像による)
  • 形式:写真は AVIF・WebP(JPEG より小さい)、図やアイコンは SVG。<picture> で対応形式を出し分けるか、画像 CDN に任せる。
  • サイズ:表示する大きさに合わせる。srcset と sizes で画面に合った大きさを選ばせる。4000px の写真を 400px で表示しない。
  • 遅延読み込み:画面の外の画像は loading="lazy"。ただし LCP になる最初の大きな画像には付けず、逆に fetchpriority="high" を付ける。
  • width・height(または aspect-ratio)を書いて場所を先に取り、CLS を防ぐ。
<img src="hero-800.avif"
  srcset="hero-400.avif 400w, hero-800.avif 800w, hero-1600.avif 1600w"
  sizes="(min-width: 768px) 50vw, 100vw"
  width="800" height="450" fetchpriority="high" alt="…">
送った JS は、取得・解析・実行のすべてに時間がかかるダウンロード回線が遅いほど長い解析・コンパイル安い端末ほど長い実行メインスレッドを占める → INPハイドレーション押せるまでの時間
JS が重いと遅くなる段階
  • バンドルアナライザーで大きいものを探し、要らないライブラリを外す・軽いものに替える。
  • ページ単位・重い部品単位でコード分割し、最初に要らないものは後から読む。
  • Server Components や静的な HTML で済む所は JS を送らない。
  • 長い処理は分割し、INP を悪くしない。

画像は同じ重さでも描画を止めませんが、JS は解析と実行でメインスレッドを使うので、同じバイト数なら JS のほうが体験への影響が大きくなります。

  • Web フォントは使う太さと文字だけに絞り(日本語は特に大きい)、font-display: swap で読み込み中も文字を出す。自分のドメインから配信し、必要なら preload。
  • 計測タグ・チャット・広告などのサードパーティは、性能を落とす大きな原因になる。本当に要るかを定期的に見直し、async/defer で読み、ユーザーの操作の後に遅らせて読むものも検討する。

あわせて、HTML や API の TTFB(CDN のキャッシュ・サーバーの処理)と、ハッシュ付きファイルの長期キャッシュも効きます。

サードパーティ見直しの観点
計測・広告のタグ本当に使っているか。タグマネージャーに溜まっていないか
チャット・ウィジェットボタンを押したときに読み込む(遅延読み込み)
埋め込み動画・地図サムネイルだけ出し、押したら本物を読み込む
ひとことで

最初の1バイトが届くまでの時間。接続・リダイレクト・サーバーの処理の合計で、LCP の土台になる。

TTFB の中身と、その後の流れDNS接続・TLSリダイレクトサーバーの処理本文のダウンロード描画ここまでが TTFB
TTFB の中身

TTFB(Time to First Byte)は、リクエストを送ってから最初の1バイトが届くまでの時間です。LCP の土台なので、ここが遅いとフロントで何をしても速くなりません。

遅い原因対策
遠いサーバー・接続の確立CDN を使う。HTTP/2・3、接続の使い回し
リダイレクトの連鎖http→https→www のような多段を1回に
SSR の中の遅いクエリ・外部 APIN+1 を直す、並列に取る、キャッシュ
毎回 HTML を作っている変わらないページは SSG・ISR・CDN のキャッシュ

TTFB の目安としては 0.8 秒以内が「良い」とされています。サーバーの処理時間は、Server-Timing ヘッダーで開発者ツールに出すと調べやすくなります。

面接ではこう答える

TTFB はリクエストを送ってから最初の1バイトが届くまでの時間で、DNS や接続の確立、リダイレクト、サーバーの処理時間の合計です。LCP はこの後に始まるので、TTFB が遅いとフロントの最適化だけでは速くなりません。対策は、CDN で近くから返すこと、リダイレクトを減らすこと、SSR の中の遅いクエリを直すこと、変わらないページを静的にしてキャッシュすることです。

ひとことで

APM とトレースで、遅い API と、その中の DB・外部 API・待ち時間の内訳を見て、原因に合った対策を選ぶ。

TTFB や API の応答が遅いときは、サーバーの中で何に時間を使っているかを分けて見ます。たいていは「アプリの処理」より「待ち時間」(DB・外部 API・ロック・接続の空き待ち)が長いです。

手順見るもの分かること
1. どの API が遅いかAPM・アクセスログのエンドポイント別の p95・p99 と件数直す順番(遅さ × 呼ばれる回数)
2. いつ遅いか時間帯・リクエスト数との関係いつも遅い=処理の問題/混むと遅い=リソースの取り合い(接続・CPU・ロック)
3. 中の内訳分散トレース(1リクエストの中の DB・外部 API・キャッシュの時間)どこで待っているか
4. DBスロークエリのログ・EXPLAIN、1リクエストのクエリ数インデックスがない・N+1・大きすぎる取得
よくある原因対策
N+1(一覧の行ごとにクエリ)まとめて取る(JOIN・IN・DataLoader)。データベースの章
インデックスがない・効いていないEXPLAIN で確かめて、WHERE・ORDER BY に合わせた複合インデックス
外部 API を順番に呼んでいる関係ないものは並列に。タイムアウトを短めに決め、遅い相手に引きずられない
毎回同じものを計算・取得しているキャッシュ(Cache-aside)。期限と消し方を先に決める
混むと急に遅くなるDB の接続プールの枯渇・スレッドの不足・ロック待ちを疑う。数を増やす前に、1件の処理を短くする
重い処理を待たせているメール送信・画像変換などはキュー(非同期)に回し、すぐ応答する

平均は少数のとても遅いリクエストを隠すので、p95・p99で見ます。手元で試すときは、本番に近い件数のデータで計ります(件数が少ないと、インデックスがなくても速く見えます)。

面接ではこう答える

API が遅いときは、まず APM でエンドポイントごとの p95・p99 と呼ばれる回数を見て、直す順番を決めます。次に分散トレースで、1リクエストの中の DB・外部 API・キャッシュの時間の内訳を見ます。多いのは N+1 やインデックスの不足、外部 API の直列呼び出しで、混むと遅くなる場合は接続プールやロック待ちを疑います。対策は、まとめて取る・インデックス・並列化・キャッシュ・非同期化です。

速くしても、機能を足すうちに少しずつ遅くなります。パフォーマンスバジェット(守る上限)を決めて、超えたら気づける仕組みにします。

何を守るか上限の例仕組み
JavaScript の量最初に読む JS を gzip 後 ○○KB までsize-limit・バンドル分析を CI で。PR に増減を表示
ラボの指標Lighthouse の性能スコア・LCP の上限Lighthouse CI を PR ごとに(数回計って中央値)
実ユーザーの指標LCP・INP・CLS の p75 が「良い」の範囲RUM のダッシュボードとアラート、Search Console
API の速さ主要な API の p95APM のアラート、SLO

ラボの計測はばらつくので、1回の結果で落とさず、複数回の中央値や「一定以上悪くなったら」で判定します。サードパーティのスクリプト(広告・分析・チャット)は、入れる前に影響を測る決まりにしておくと、気づかないうちに重くなるのを防げます。

この章のまとめ

🖨 まとめシート(印刷・PDF)
  • Core Web Vitals(LCP・INP・CLS)の意味と改善方法
  • 測ってから直す(Lighthouse・実ユーザーの計測)

この章のクイズ

面接ではこう聞かれる

まず答えを見ずに声に出して答え、そのあと開いて比べる。

Q1ページが遅いと言われたらどう調べますか?

模範回答

まず「遅い」を数字にします。どのページの、どの端末・回線のユーザーが遅いのかを、RUM の Core Web Vitals や Search Console で確かめ、LCP・INP・CLS のどれが悪いかを見ます。

次に原因を特定します。LCP が悪ければ開発者ツールのウォーターフォールで、TTFB が長いのか(サーバー・DB・キャッシュ)、画像が大きいのか、描画を止める CSS や JS があるのかを見ます。INP なら Performance パネルで長いタスクを探し、どの JS が重いかを調べます。原因に合わせて、画像の最適化、コード分割、キャッシュ、遅いクエリの修正などで直し、最後に同じ条件で測り直して効果を確かめます。RUM でも数日見て、改善が実ユーザーに出ているかを確認します。

深掘りされたら

  • TTFB が遅い原因はどう切り分けますか?

見られているポイント指標で測る→原因を特定→直す→測り直す、の流れ

Q2LCP・INP・CLS とは何ですか? それぞれ何で悪くなりますか?

模範回答

3つとも Core Web Vitals の指標です。LCP は一番大きな画像や文章が表示されるまでの時間で、良いのは 2.5 秒以内です。遅いサーバーの応答、大きな画像、描画を止める CSS や JS で悪くなります。INP はクリックなどの操作から画面が変わるまでの時間で、良いのは 200ms 以内です。メインスレッドを長く占める JS が主な原因です。

CLS は表示中にレイアウトがどれだけずれたかで、良いのは 0.1 以下です。サイズを指定していない画像、後から入る広告やバナー、Web フォントの切り替えで悪くなります。判定は実ユーザーの 75 パーセンタイルで見ます。

深掘りされたら

  • INP はラボの計測で測れますか?

見られているポイント3つの意味と目安、悪くなる原因

Q3画像の多いページを速くするには、何をしますか?

模範回答

まず形式とサイズです。写真は AVIF や WebP にし、表示する大きさに合わせて srcset と sizes で画面に合ったものを選ばせます。画像 CDN に変換を任せると手間が減ります。

次に読み込む順番です。画面の外の画像は loading="lazy" で後回しにし、LCP になる最初の大きな画像には lazy を付けず fetchpriority="high" を付けます。width と height を書いて場所を先に取り、CLS を防ぎます。最後に、ハッシュ付きの URL で長くキャッシュさせます。直す前後で LCP と転送量を測って効果を確かめます。

深掘りされたら

  • LCP の画像に lazy を付けるとどうなりますか?

見られているポイント形式・サイズ・読み込み順・CLS

Q4API の応答が遅いと言われました。どう調べて、どう直しますか?

模範回答

まず APM やアクセスログで、どの API がどれだけ遅いか(p95・p99)と呼ばれる回数を見て、影響の大きいものから手を付けます。いつも遅いのか、混む時間だけ遅いのかも見ます。次に分散トレースで1リクエストの内訳を見て、DB・外部 API・アプリの処理のどこで時間を使っているかを特定します。

例えば DB で、一覧の行ごとにクエリを投げる N+1 なら、まとめて取るように直します。インデックスがなければ EXPLAIN で確かめて追加し、外部 API を順番に呼んでいれば並列にしてタイムアウトを決めます。混むと遅くなるなら接続プールの枯渇やロック待ちを疑います。直したら同じ条件で前後を計り、リリース後も p95 を確かめます。

深掘りされたら

  • 平均ではなく p99 を見るのはなぜですか?
  • キャッシュを入れるときに先に決めておくことは?

見られているポイント推測ではなく計測から入るか。APM・トレース・スロークエリで原因を絞り、原因に合った対策を選べるか。