はじめに

2021年7月から8月末までの2ヶ月間、ABEMA で内定者アルバイトをしていた 吹金原(@progriro)です。トレーナーの磯部さん(@ebosi38051)と野口さん(@nodaguti)をはじめ、ABEMA Web チームのみなさんには大変お世話になりました。ありがとうございました。

私は2ヶ月間で、UI 改善やリファクタリング、0からのパフォーマンス改善など、さまざまなタスクに取り組ませていただきました。”0からのパフォーマンス改善” と表記した理由は、既に Issue などで指定されていた箇所を改善したのではなく、「どこを改善すべきか」から決めて改善まで行ったためです。

本記事では、私が取り組んだタスクの中で最も成果を挙げることができた「ブラウザ版 ABEMA のパフォーマンス改善タスク」について紹介します。

パフォーマンスの分析と改善を繰り返した結果、エピソードページという種類のページにおいて、Core Web Vitals の指標のひとつ CLS (ページ読み込み時にコンテンツが動いた度合い) を 0.05 から 0.02 まで改善することができました。

また、Resource Hints を指定することで「Web ページのロード時間に発生するネットワーク処理に費やす時間を軽減する」パフォーマンス改善を行いました。

本記事では、それぞれの改善タスクに対してどのように向き合い、どのような改善を行なったのか、できるだけ詳細に述べていきたいと思います。

※ Core Web Vitals の指標の詳細については、以下をご覧ください。

 

対象読者

  • パフォーマンス改善に興味がある方
  • パフォーマンス改善を行なったことがない方
  • パフォーマンス改善のプロセスを知りたい方

 

ABEMA Web チームの紹介

本題に入る前に、私が所属していた ABEMA Web チーム(以下、Web チーム)について紹介します。

私は、ブラウザ版 ABEMA の開発を行っている Web チームでお世話になりました。ABEMA はサイバーエージェント内で最も大きい組織であり、Web チームはその中に属するチームのひとつです。20名前後のフロントエンドエンジニアが所属し、日々直面する課題やプロダクト改善、新規機能開発を行っています。本記事公開時点での ABEMA Web の Issue 数は 1400 以上あることからも、ABEMA の規模の大きさと、エンジニア組織の規模の大きさがわかります。

私が感じた Web チームの特徴は「品質の高いプロダクトを重視するチーム」「複数のチームが存在し、各々の担当分野に責任を持つチーム」「雰囲気が和やか・お互いをリスペクトしているチーム」の3つです。

ABEMA の WAU(1週間あたりの利用者数)は 約1825万 です。多くの方が愛しているサービスだからこそ、品質の高いプロダクトを重視する傾向にあると感じました。また、Web チームは更に複数のチームに別れており、各々がそれぞれのチームの担当分野に責任を持つというチーム構成です。チームごとの分野に精通したエンジニアが共同しあうことで ABEMA が成り立っています。

異なる専門知識を持ったエンジニアが在籍している Web チームですが、朝会では1人1人が近況報告や雑談を行ったり、社員エンジニア同士でランチ(コロナ禍のためオンライン)をしていたり、週1回の勉強会を Web チームで行っていたり、チームの雰囲気はとても和やかで、お互いをリスペクトする姿勢を持ちながら開発が行われていました。

ABEMA web team の特徴3つ

そんな雰囲気の ABEMA Web チームでは、野口さんを中心に「少数の職人ではなく組織全体が改善に取り組む文化の大切さ」が認知されており、パフォーマンス改善のサイクルを回し続ける基盤が整っています。

Synthetic Monitoring / Real User Monitoring の分析基盤が整っていることに加えて、計測方法や改善する優先度も決まっているため、改善を行うときはもちろん、興味・関心を持ったときでもすぐにパフォーマンス状態を確認できます。また、改善に取り組む大切さを伝えるために、計測結果を CI に組み込んだり、計測が終わったタイミングで Slack などの社内連絡ツールに自動的に投稿する仕組みが作られていたり、週1回のパフォーマンス週報や定期的なパフォーマンス振り返りミーティングなどが行われています。

組織全体がパフォーマンス改善の大切さを認識し、継続的にパフォーマンス改善が行える基盤が整っていることは ABEMA Web チームの強さであると同時に、現在の ABEMA を支えている大切な要素の1つだと実感しました。

より詳しく知りたい方は、野口さんが執筆された 「データドリブンなパフォーマンス改善 〜 一過性で終わらせない文化の作り方 〜:WEB+DB PRESS Vol.124|技術評論社」 をご一読ください。

 

ABEMA Web システム構成

ABEMA Web の簡単なシステム構成を示します。実際はもう少し複雑なシステム構成になっていますが、本記事の本題ではないため簡略化した図としました。

SSR 時は、abema-web-backend(SSR Server)が gRPC 経由でデータを取得し HTML を生成、その後クライアントで hydration を行っています。この hydration 時に CLS が発生してしまうケースがあり、本記事で取り上げている CLS 改善はこれに該当します。

hydration を行うためにクライアントでは abema-gateway(API Server)からデータを取得しています。しかし、abema-web-backend と abema-gateway では取得できる情報量に差があり、UserId ごとにパーソナライズされた結果は abema-gateway からしか取得できません。この差に対して、SSR 時に正しく Placeholder を設定していないと、hydration 時に CLS が発生してしまいます。

ABEMA web システム構成

 

パフォーマンス改善

冒頭でも述べていますが、私は CWV の指標のうちの CLS に該当する改善タスクと LCP に該当する改善タスクを行いました。それぞれを「CLS 改善」と「Resource Hints の指定」と題し、それぞれの改善プロセスと改善結果を、以下に述べていきます。

※ Resource Hints はあまり LCP にインパクトをもたらさなかった(おそらく別の箇所が大きなボトルネックになっていて、Resource Hints の改善はそれに覆い隠されてしまった)ので、明示的に LCP 改善タスクとはせず、「Resource Hints の指定」と表現しました。

 

CLS 改善

今回行った CLS 改善は、以下のような手順で行いました。綺麗な順番で示していますが、実際には『改善の実装を行なってから本当に改善されたかを確認するサイクル(以下、改善サイクル)』を何度も繰り返し、最も良く改善されたものを開発環境 / 実環境にマージしています。

  1. NewRelic Browser で CLS が発生しているページを特定
  2. 実際のページにアクセスし、CLS が発生している箇所を特定
  3. どのようにして CLS 改善を行うか計画を立てる
  4. 計画に沿って計測を行いながら CLS を改善
    • ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認
    • 開発環境に対する Synthetic Monitoring を介して、改善できているかを確認
  5. 実環境に対する Real User Monitoring を介して、改善できているかを確認
  6. お祝いをする 🎉

※ NewRelic は Real User Monitoring に該当し、計測された field data を元に分析を行うことができる SaaS サービスです。

開発環境では Real User Monitoring を使用できないため、必然的に Synthetic Monitoring になります。そのため、今回はまず NewRelic(Real User Monitoring)で計測された field data から、ビジネスインパクトの大きい改善すべきページを選択し、そのページに対して Chrome Devtools や Lighthouse(Synthetic Monitoring)で lab data を計測しながら、改善サイクルを回しました。

NewRelic で CLS が発生しているページを特定

まず初めに、CLS 改善を行うページ(CLS が発生しているページ)を決定します。前述した通り、ABEMA Web では Real User Monitoring に NewRelic を用いています。NewRelic の計測結果を元にどのページで CLS が発生しているのかを探していくのですが、ここで1つ意識すべき点があります。それは、プロダクト(本記事では ABEMA Web)において重要なページであるか、ユーザーアクセス数が多いページであるかという視点を持つことです。

仮に NewRelic で CLS が発生しているページを発見したとしても、プロダクトにおいてそのページの重要度が低かったり、ユーザーアクセス数が少ないページだった場合は、改善にかかったコストと改善による効果の大きさが釣り合わず、改善はしたものの費用対効果(ROI)が低い状態になってしまう可能性が高くなります。

そのため、改善すべきページの優先順位をあらかじめ定めておくことがとても大事です。その優先順位を意識しつつ、NewRelic といった Real User Monitoring の計測結果から改善すべきページを決定します。

ABEMA Web では、改善すべきページの優先順位が以下のように定められていました。

  • モバイル優先(ユーザー数が モバイル > デスクトップ であるため)
  • ユーザーが多く訪れているページ優先
  • NewRelic の数値とアクセス数が多いページ優先

上記のように決められていた優先順位を考慮しつつ、CLS が発生しているページを探していきました。もちろん、探していく過程で複数の改善できそうなページを見つけることがあります。そのときは、より CLS の数値が高いページやアクセス数の多いページを改善対象としたり、自分のスキルで改善できそうか / そもそも実装上改善できるものなのかを、実際のコードを読みながら決定していきます。(※ 改善対象の決定はこれが全てではありません)

上記のプロセスを行った結果、私はエピソードページ(https://abema.tv/video/episode/:id)を改善することにしました。追加条件として、ABEMA Web のメインユーザーは無料会員かつモバイルユーザーであるため、1番多く閲覧され得る「モバイル端末かつ無料会員で発生している CLS」を改善対象としました。

実際のページにアクセスし、CLS が発生している箇所を特定

エピソードページは、API Server からデータを取得している最中(SSR 時)に Placeholder を表示させています。以下の gif は、改善前のエピソードページです。

Free Video TVOD
CLS 改善前の Free Video CLS 改善前の TVOD

上記を見てわかるように、Placeholder の表示とデータ取得後の表示に差異があり、CLS が発生していることがわかります。Placeholder から想定される表示位置と実際の表示位置が異なっているため、ユーザーが誤って操作してしまう可能性が高くなります。

どのようにして CLS 改善を行うか計画を立てる

前提知識として、ABEMA には以下4つの動画形態が存在しています。

  • Free Video(無料動画)
  • Premium Video(プレミアム動画)
  • Free TVOD(期間限定無料レンタル動画)
  • TVOD(有料レンタル動画)

上記4つの動画形態の UI を確認したところ、SSR 時に表示される Placeholder は、動画形態に関わらず全て同じものが表示されていました。

しかし、動画形態が異なればデータ取得後に表示される UI は変わってきます。動画形態が Free Video の場合、無料会員でも視聴が可能なため「アプリ(無料)で視聴する」ボタンが表示されます。一方、動画形態が Premium Video の場合、「まずは2週間無料トライアル」ボタンと共に、動画タイトル・アイキャッチ画像などが表示されます。

Free Video Button Premium Video Button
Free Video ボタン:アプリ(無料)で視聴する Premium Video ボタン:まずは2週間無料トライアル

そのため、SSR 時に表示される Placeholder と データ取得後に表示される UI で差分が生じており、この差分によって CLS が発生していたことがわかりました。

したがって、上記4つの動画形態それぞれに適した Placeholder を出し分けることで、CLS 改善ができると想定できます。

これで CLS 改善ができる状態になりました。ここまでで行なったことは以下の3つです。

  1. NewRelic Browser で CLS が発生しているページを特定
  2. 実際のページにアクセスし、CLS が発生している箇所を特定
  3. どのようにして CLS 改善を行うか計画を立てる

この後は、上記で立てた計画に沿って計測を行いながら CLS 改善を行っていきます。

計画に沿って計測を行いながら CLS 改善

実際に UI の状態を見ながら改善を行う必要があるため、Synthetic Monitoring で計測しながら改善を進めていきます。Chrome Devtools を使用して CLS が発生している箇所と数値を確認しながら、4つの動画形態それぞれに適した Placeholder を出し分けていきます。

Free Video 改善前の Devtools

Free Video / Premium Video の CLS 改善

ABEMA Web では classnames というライブラリを用いています。条件によって任意のスタイルを当てられるようになっているため、isFree(Free Video か否か)によってスタイルの出し分けを行いました。

<div
  className={classNames('com-account-LeadPremiumPlaceholder', {
    'com-account-LeadPremiumPlaceholder--free': isFree,
  })} 
> 
  {/* Placeholder */} 
</div>

加えて、既存の Placeholder とデータ取得後の UI で差異が生じていたため、細かいスタイルの調整を行いました。個人的には、デグレを発生させないように細かなスタイル調整をしていた時間がとても大変でした。

これらの実装によって Free Video と Premium Video で発生していた CLS を改善することができました。以下が、無料ユーザーかつモバイル端末で計測した結果をまとめた表と、改善前後の比較 gif になります。

Before After Result
Free Video 0.06579 0 改善
Premium Video 0.01583 0 改善
Free TVOD 0.06055 0.07438 若干悪化
TVOD 0.04930 0.08214 悪化
Free Video 改善前 Free Video 改善後
CLS 改善前の Free Video CLS 改善後の Free Video

ここまでで行なったことは以下の2つです。

  1. 計画に沿って計測を行いながら CLS を改善
    • ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認

Free TVOD / TVOD の CLS が悪化した原因調査

Free Video と Premium Video で発生していた CLS を改善することはできましたが、Free TVOD と TVOD で発生していた CLS は逆に悪化してしまいました。

Before After Result
Free Video 0.06579 0 改善
Premium Video 0.01583 0 改善
Free TVOD 0.06055 0.07438 若干悪化
TVOD 0.04930 0.08214 悪化
Free TVOD TVOD
CLS が悪化した Free TVOD CLS が悪化した TVOD

結論から述べると、悪化した原因は2点ありました。

1つ目の原因は、TVOD の Placehodler でも Premium Video の Placehodler が適用されてしまっていたことでした。

「Free Video / Premium Video の CLS 改善」では isFree という変数を用いて Placeholder の出し分けを行なっていましたが、isFree だけでは SSR 時に 4つの動画形態全ての出し分けを行うことができていませんでした。

動画形態 SSR 時の isFree
Free Video true
Premium Video false
Free TVOD false
TVOD false

上表より、isFree は Free Video / Premium Video の判定はできるが、Free TVOD / TVOD の判定はできないことがわかります。つまり、isFree は ABEMA Web において「Free Video かどうかを判定する変数」であり「Free TVOD かどうかを判定する変数」ではないということです。

早速「Free TVOD かどうかを判定する変数」が存在しているかを調査し、onDemandType という変数が存在していることがわかりました。しかし、ここで 2つ目の原因が見つかりました。

2つ目の原因は、「Free TVOD かどうかを判定する変数」(以下、onDemandType)が SSR 時に取得できておらず、Free TVOD / TVOD それぞれに適した Placeholder を出し分けることができていなかったことでした。

... 
abema-web-backend | transcodeVersion: 1, 
abema-web-backend | terms: [], 
abema-web-backend | freeTerms: [], 
...

abema-web-backend のログ:本来 terms 内にあるはずの onDemandType が存在していない

 

動画形態 SSR 時の isFree SSR 時の onDemandType
Free Video true
Premium Video false
Free TVOD false
TVOD false

この後、なぜ CSR 時は onDemandType が取得できていて、SSR 時は onDemandType が取得できていないのかを調査しました。

冒頭で示した「ABEMA Web システム構成」より、SSR 時 / CSR 時どちらにおいても gRPC 経由でデータを取得していることがわかります。もちろん、abema-web-backend の実装に問題があることを想定してトレースを行いましたが、特に問題はなさそうでした。

ここまでで、CSR 時は onDemandType を取得できていて、SSR 時は onDemandType を取得できていない原因が gRPC 周りにありそうだと当たりをつけることができます。

ABEMA web システム構成 1

そこで、abema-web-backend の .proto ファイル と abema-gateway の .proto ファイル を比較してみたところ、abema-web-backend の .proto ファイル は古い内容であり、かつ abema-web-backend の .proto ファイル には TVOD に関する定義が存在していませんでした。

ABEMA web システム構成 2:proto file で齟齬が生じていた

更新前の proto file:onDemandType が abema-web-backend に存在していない

ここまでの調査から、abema-web-backend の .proto ファイルが 最新の .proto ファイル より古いものだったため、TVOD に関するデータ(onDemandType, etc…)を受け取れていなかったことがわかりました。

Free TVOD / TVOD の CLS 改善

Free TVOD / TVOD の CLS が悪化してしまった原因は「SSR 時に TVOD 関連のデータを取得できていなかった」ことにあったため、TVOD 関連のデータ(onDemandType, etc…)を SSR 時に取得できるように修正し、取得したデータによって Placeholder を出し分ける実装を行いました。

  1. .proto ファイル を最新のものに更新
  2. SSR 時に TVOD に関するデータ(onDemandType, etc…)を含める
  3. onDemandType を用いて Placeholder の出し分けを行う

ABEMA web システム構成 3:proto file を修正

更新後の proto file:onDemandType を abema-web-backend に追加

動画形態 SSR 時の isFree SSR 時の onDemandType
Free Video true Free Video | Free TVOD
Premium Video false Premium Video
Free TVOD false Free Video | Free TVOD
TVOD false TVOD

この実装によって Free TVOD と TVOD で発生していた CLS も改善することができ、改善後の CLS を全て 0(CLS が全く発生しない状態)にすることができました。なお、ローカル環境で改善を確認した後、開発環境でも改善の確認を行いました。

以下が、無料ユーザーかつモバイル端末で計測した結果をまとめた表と、改善前後の比較 gif になります。

Initial Before After Result
Free Video 0.06579 0 0 改善
Premium Video 0.01583 0 0 改善
Free TVOD 0.06055 0.07438 0 改善
TVOD 0.04930 0.08214 0 改善
Free TVOD 改善前 Free TVOD 改善後
CLS が悪化した Free TVOD CLS 改善後の Free TVOD
TVOD 改善前 TVOD 改善後
CLS が悪化した TVOD CLS 改善後の TVOD

ここまでで行なったことは以下の通りです。最初の実装と合わせると、計測と改善を繰り返している(以下、♻️)ことが分かるかと思います。

  1. 計画に沿って計測を行いながら CLS を改善 ♻️
    • ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認 ♻️
    • 開発環境に対する Synthetic Monitoring を介して、改善できているかを確認

実環境に対する Real User Monitoring を介して、改善できているかを確認

以下は、Real User Monitoring(NewRelic)の CLS 計測結果グラフです。

リリース日には、0.05 から 0.02 に改善されていることが計測できました。0 ではなく 0.02 になっている理由は、今回の CLS 改善対象が 無料ユーザーのみ であったことから、有料ユーザーの CLS がまだ残っているからだと考えられます。(※ 0 となっている部分は「データ保持期限外なのでデータがない」ことを意味しています)

改善後の RUM グラフ:CLS が 0.05 から 0.02 に改善

リリース日:0.5 から 0.2 に改善

 

リリースから約2週間後の計測でも、安定して 0.02 となっていることが確認できています。

改善から2週間後の RUM グラフ:CLS が 0.05 から 0.02 に改善

リリースから約2週間後:安定して 0.02 がキープされている

 

この結果が出たとき、トレーナーの磯部さんと野口さんはもちろん、ABEMA Web チームの方がお祝いしてくれました。少しでもパフォーマンスが改善したらチームで盛り上げる ABEMA Web 文化はとても素晴らしく、改善者のモチベが上がる大切な文化だと感じました。

CLS が改善されたことを Slack でお祝い

 

最後に行なったことは以下の2つです。

  1. 実環境に対する Real User Monitoring を介して、改善できているかを確認
  2. お祝いをする 🎉

CLS 改善 まとめ

機能開発などと比較した場合、パフォーマンス改善は地道な作業の連続だと思います。しかし、パフォーマンスは現代の Web サービスにおいて重要な要素です。パフォーマンスの大切さを組織全体で認識し、実際に改善ができる基盤が整っていることは、より良い Web サービスを提供することに繋がると実務を通して学ぶことができました。全ての組織が ABEMA Web チームのような基盤を整えることは難しいかもしれませんが、今回学んだことを取捨選択して今後の開発に活かしていきたいと思います。

本タスクで示した以下の改善手順・内容は全ての改善に当てはまるとはいえませんが、現在パフォーマンス改善に興味・関心がある方はもちろん、今後パフォーマンス改善を行う方の1つの参考になれば嬉しいです。

  1. NewRelic Browser で CLS が発生しているページを特定
  2. 実際のページにアクセスし、CLS が発生している箇所を特定
  3. どのようにして CLS 改善を行うか計画を立てる
  4. 計画に沿って計測を行いながら CLS を改善
    • ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認
    • 開発環境に対する Synthetic Monitoring を介して、改善できているかを確認
  5. 実環境に対する Real User Monitoring を介して、改善できているかを確認
  6. お祝いをする 🎉

 

Resource Hints の指定

※ CLS 改善に時間を要したため、本タスクは途中でトレーナーの野口さんに引き継がせていただきました。

本章で取り上げるタスクは「全ページで必要となる origin に dns-prefetch / preconnect を指定する」というものです。dns-prefetch / preconnect は、Resource Hints という仕様で定められている投機的な処理になります。

本タスクを行なった理由は、Lighthouse のリコメンデーションに出ていたため・ボトルネックの箇所が今後改善されたときに LCP によい影響を出すためです。

本題に入る前に、Resource Hints について簡単に説明します。

Resource Hints について

Rersource Hints とは、Web ページのロード時間に発生するネットワーク処理に費やす時間を軽減するために策定されている仕様のことです。次に示しているものは Resource Hints の仕様で定められている4つの投機的な処理です。

  • dns-prefetch:事前に DNS ルックアップ まで行う
  • preconnect:事前に DNS ルックアップ / TCP ハンドシェイク /(HTTPS への接続の場合は)TLS ネゴシエーション まで行う
  • prefetch:遷移先で使用するため、事前にリソースをダウンロードしてキャッシュする
  • prerender:遷移先を事前にロードし、レンダリングする

link 要素で rel=’dns-prefetch’ などと指定することで、Web ページでこれから必要になるリソースであることをブラウザに事前に伝えことができます。ただし、Resource Hints はあくまでも示唆のため、処理の保証はないことに注意が必要です。

preconnect について

preconnect はいくつも使うとファーストビューを遅らせる可能性があるため、特に重要なものだけを指定することが推奨されています。重要なものについて、web.dev では「接続しないと先の処理に進めない(待ち合わせが発生する)ドメイン」を重要なものと捉えており、こういったドメインを優先的に指定するべきだと言及されています。また、他の処理にも言えることですが、特に preconnect は head の最初に読み込んだ方が有効です。

参考:Establish network connections early to improve perceived page speed

crossorigin 属性について

crossorigin 属性は、CORS (Cross Origin Resource Sharing) の振る舞いに関する設定を行う属性であり、request mode と credentials mode を規定します。詳しい説明は、crossorigin 属性の仕様を読み解く をご参照ください。

request mode

cors mode / no-cors mode どちらのモードでリクエストを投げるかを決めます。same origin ではないドメインへの通信は cors ポリシーによりブロックされ、その旨がコンソール等に表示されます。ここで、request mode を no-cros にすると opaque response を得られますが、実際は意味を持たない虚無なレスポンスです。つまり「通信は行うが、意味ある値を返さずに cors エラーが抑制されるモード」だと言えます。

  • crossorigin 属性を指定:cors mode
  • crossorigin 属性を指定しない:no-cors mode
credentials mode

cross-origin リクエストを投げる時の credentials mode を決めます。Fetch 仕様 では「credentials とは、HTTPクッキー、TLSクライアント証明書、および認証エントリ(HTTP認証の場合)のこと」と定義されています。

  • omit:常に credentials を送信しない
  • same-origin:同じドメインの場合は credentials を送信する
  • include:常に credentials を送信する

rel="dns-prefetch" の場合は、request mode と credentials mode の両方意味を持ちます。ただし、取得された DNS データはあらゆるコネクションについて再利用されるので、credentials の指定が必要なく、 crossorigin 属性も不要となります。

rel="preconnect" の場合は、credentials mode のみ意味を持ちます。preconnect は、credentials で分割されているコネクションプールに事前にコネクションを張って保存しておくための hints であるが故に、どちらのモードのコネクションを確立しておきたいのか指定する必要があります。よって、これから事前確立するコネクションが credentialed リクエストのためのものなのか、それとも uncredentialed リクエストのためのものなのかを指定する手段として crossorigin 属性が提供されています。

crossorigin 属性についての知識を得たところで、本題に入ります。

改善手順について

本タスクは、以下の手順で進めました。綺麗な順番で示していますが、CLS 改善と同様に改善サイクルを何度も回し、最も良く改善されたものを開発環境 / 実環境にマージしています。

  1. 全ページで必ず必要になるドメインを調査
  2. 対象ドメインに Resource Hints の処理を適用
    • 重要度の高いドメインに preconnect を指定
    • 重要度の低いドメインを dns-prefetch に指定
    • 必要に応じて crossorigin 属性を適用
  3. ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認
  4. 実環境に対する Real User Monitoring を介して、改善できているかを確認

開発環境では Real User Monitoring を使用できないため、Chrome Devtools や Lighthouse で lab data を計測しながら、改善サイクルを回しました。

全ページで必ず必要になる domain を調査

Resource Hints を指定する対象として有効だと考えられるドメインを探します。Resource Hints の指定は初の試みだったこともあり、今回は「全ページで必要になるドメイン」を調査対象としました。調査では、主に Chrome Devtools の Network タブを利用しました。

「preconnect について」で言及した通り、preconnect を指定するべき重要度の高いドメインと、それ以外の重要度の低いドメインに分ける必要があります。そこで、web.dev で述べられていた「接続しないと先の処理に進めない(待ち合わせが発生する)ドメイン」を重要度が高いドメインと定義することにしました。

結果は以下の通りです。

重要度 対象ドメイン数
4
11

対象ドメインに Resource Hints の処理を適用

対象ドメインに対して Resource Hints の処理を適用していきます。

今回は preconnect / dns-prefetch それぞれに対応した配列を定義し、その配列に対象ドメインを指定しました。

const preconnect = [
  // domain ...
];

const dnsPrefetch = [
  ...preconnect,

  // Google 
  '//googleads.g.doubleclick.net', 
  '//www.googleadservices.com', 
  '//www.googletagmanager.com', 
  '//www.google-analytics.com', 
  '//www.google.co.jp', 
  '//www.google.com', 

  // Other domain ...
];

そして、定義した配列に対して Array.map を使用して Resource Hints が指定された link 要素を生成。最終的に AppResourceHints というコンポーネントとし、これを head 最上部で呼び出しています。

export function AppResourceHints() {
  return (
    <>
      {dnsPrefetch.map((origin, i) => ( 
        <link key={`dns-prefetch-${i}`} rel="dns-prefetch" href={origin} />
      ))} 
      {preconnect.map((origin, i) => (
        <link 
          key={`preconnect-${i}`} 
          rel="preconnect" 
          href={origin} 
          crossOrigin="anonymous" 
        />
      ))}
    </> 
  );
}

rel="dns-prefetch" は、取得された DNS データはあらゆるコネクションについて再利用されるので、credentials の指定が必要なく crossorigin 属性も不要となります。そのため、上記では crossorigin 属性を指定していません。

rel="preconnect" は、target origin = cors かつ credentials mode = same-origin とすることで uncredentialed リクエストのための preconnect として定義できます。そのため、上記では crossOrigin="anonymous" を指定しています。

Resource Hints 適用後の head

AppResourceHints コンポーネント適用後の head

ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認

Lighthouse で改善できたかを確認した結果が以下の通りです。 残念ながら、著しい改善にはなっていませんでした。Resource Hints の指定によって著しい改善ができなかったのは、おそらく別の箇所が大きなボトルネックになっていて、Resource Hints の改善はそれに覆い隠されてしまったためだと考えています。

Resource Hints 適用後の Lighthouse

実環境に対する Real User Monitoring を介して、改善できているかを確認

NewRelic でも改善できたかを確認しましたが、やはり著しい改善にはなっていませんでした。

Resource Hints 指定 まとめ

改善タスクでは、CLS 改善のように大きく改善することもあれば、本タスクのように全く改善しないことがあります。全く改善しなかった場合は特に、なぜ改善しなかったのかを振り返り、別途施策を立てていく活動などが大切だと考えます。

本タスクで示した以下の改善手順・内容は全ての改善に当てはまるとはいえませんが、現在パフォーマンス改善に興味・関心がある方はもちろん、今後パフォーマンス改善を行う方の1つの参考になれば嬉しいです。

  1. 全ページで必ず必要になるドメインを調査
  2. 対象ドメインに Resource Hints の処理を適用
    • 重要度の高いドメインに preconnect を指定
    • 重要度の低いドメインを dns-prefetch に指定
    • 必要に応じて crossorigin 属性を適用
  3. ローカル環境に対する Synthetic Monitoring を介して、改善できたかを確認
  4. 実環境に対する Real User Monitoring を介して、改善できているかを確認

 

最後に

2ヶ月間で、Web パフォーマンスに関する知識や改善プロセスはもちろん、パフォーマンス改善を行うべき組織のあり方も知ることができました。

専門的な知識を持ったチームがまるで細胞のように自立して活動し、ABEMA Web チーム 全体がまるで1つの生命体のように大きな力を発揮しているように感じました。そのような環境で経験を積むことができたことは私にとって貴重な経験となりました。加えて、パフォーマンス改善はフロントエンドに限らずバックエンドの協力も必要だということや、パフォーマンスに関する知識・改善プロセスを実務を通して学ぶことができたことは、私にとって大きな成長となりました。

改めまして、多くの知識と経験を積ませていただいたトレーナーの磯部さんと野口さんをはじめ、暖かく迎えてくださった ABEMA Web チームのみなさんには大変お世話になりました。ありがとうございました。

本記事が、パフォーマンス改善に取り組む方々の一助になりますと幸いです。

 

告知

12月17日(金)「ABEMA Developer Conference 2021」がオンライン開催されます。Webチームからは「専任チームで実現する継続的なWebパフォーマンス改善」についての発表が行われます。全体のタイムテーブルはこちらです。ぜひご覧ください。

専任チームで実現する継続的なWebパフォーマンス改善