AMP におけるパフォーマンス改善

AMP におけるパフォーマンス改善

はじめまして、ホットペッパービューティーコスメ(以下 HPBC) の Web フロントエンドエンジニアとして学生アルバイトをしている三島です。3 ヶ月間のアルバイト期間で、案件に関わる機能開発や SEO 施策の検討と実装、パフォーマンス改善など様々な業務に取り組ませていただきました。本記事ではその中でも、私が取り組んだ HPBC におけるパフォーマンス改善について紹介します。

目次

1. はじめに
2. 想定読者
3. AMP とは
4. パフォーマンス改善の前提知識
5. 現状計測
6. 問題特定
7. 問題解決
8. 今後
9. まとめ

はじめに

HPBC の開発プロジェクトでは、Web ページを閲覧してくれるユーザーにとってページが完全に表示されるまでのスピード、つまり「パフォーマンスが大事である」という認識をチーム全体で共有しています。Web サービスのプロダクトは、機能の追加・改修が行われていくことでパフォーマンスが低下する傾向にあります。HPBC でも様々な機能追加が行われているため、初期リリースの頃から継続的なパフォーマンス改善のための取り組みを行っています。
HPBC の Web フロントエンドでは、AMP と Next.js を利用して開発を行っており、AMP Valid に近い状態を保ち続けるように開発しています(AMP Valid については後述します)。Google がページ高速化のために提唱している AMP を取り入れることで HPBC でも高速な Web ページを提供し続けられると考えていましたが、AMP をただ取り入れるだけでは自分たちが求めている速度を達成することはできませんでした。今回は、AMP を利用している HPBC がどのようにパフォーマンス改善を行ったのかについて紹介します。

想定読者

  • AMP に興味がある、もしくは利用している人
  • パフォーマンス改善に興味がある人

AMP とは

AMP(Accelerated Mobile Pages)は、Google が提供する、モバイルに特化したユーザーファーストな Web コンポーネントフレームワーク です。AMP が提唱しているルールに従った Web ページは AMP Valid (有効)なページと判断され、Google が提供する AMP 用の CDN(AMP Cache)にのり、ブラウザの検索結果から高速なページ遷移が可能になります。
AMP の開発パターンは大きく2種類あります。1 つ目は、Hybrid AMP と呼ばれるもの、2 つ目は Full AMP、もしくは AMP First と呼ばれるものです。HPBC では、全てのページを AMP Valid に近い状態としており、いわゆる Full AMP での開発を行っています。それぞれの開発パターンなど AMP の詳しい内容や HPBC でどのような使い方がされているのかについては以下の記事に詳しく書かれているのでご参照ください。

モダンなWebフロントエンドの技術とAMP

パフォーマンス改善の前提知識

具体的なパフォーマンス改善の話に移る前に、HPBC がどのようなシステム構成か説明します。また、 Web パフォーマンスの基本事項についても簡単に紹介しておきます。

HPBC のシステム構成

HPBC では最前段に CDN を配置しています。基本的に全てのリソースは CDN でキャッシュしているため、ユーザーからアクセスされたページはそれ以降高速に返すことができます。オリジンでは Next.js を利用しています。Next.js は、バックエンドの API サーバから Web ページの描画に必要な情報を取得し、HTML の生成を行っています。
HPBC では Next.js の AMP オプションを利用しています。以下のように1行書くだけで AMP 用のスクリプトや CSS などの必要なコードをビルド時に自動的に挿入してくれます。それに加えて、AMP 向けのコードをさらに最適化してくれる amp-optimizer というツールを内部で自動的に使ってサーバサイドレンダリングを行います。

コード参照

HPBC では AMP Cache を利用していません。そのためにあえて一部分だけ AMP Valid ではない状態にしています。具体的には、html タグの ⚡️ (イナズマ)マークだけを取り除いた形で HTML の生成を行い、AMP のバリデータをカスタマイズしてこの部分だけ許容するようにしています。 AMP Cache を利用していない理由は、AMP Cache のレスポンスが Google ドメインになってしまうため、分析観点で不都合が生じるからです。この問題については、Signed HTTP Exchange と呼ばれる方法で対応可能ですが、世の中の全てのブラウザが対象のものではありません。

パフォーマンスの基本事項

Chrome DevTools

Chrome ブラウザで利用できる開発用ツールです。例えば、ブラウザが Web ページを表示するまでに行われる処理を記録して、どのような通信が行われたのか、また、どのようなコンテンツが描画されているかなど、Web ページそのものを見るだけでは分からない様々な詳しい情報を取得することができます。直接データを変更して確認することもできるので、これを用いてパフォーマンスにおけるボトルネックを探し、パフォーマンス改善を行うことができます。

Web メトリクス

ここでは当記事で登場する 4 つのメトリクスについて紹介します。

DCL は、Dom Content Loaded の略です。最初の HTML ドキュメントが読み込まれ、解析が完了したタイミングを測ります。
FP は、First Paint の略です。色や線を含む何らかの視覚要素が表示されたタイミングを測ります。
FCP は、First Contentful Paint の略です。何らかのコンテンツが表示されたタイミングを測ります。
LCP は、Largest Contentful Paint の略です。Core Web Vitals の指標の 1 つで、ブラウザの表示範囲内で最もサイズの大きなコンテンツが表示されたタイミングを測ります。

パフォーマンスバジェット

Web ページの低速化する原因の1つには、不要な JavaScript や CSS などが機能追加や改修で残ってしまうことでファイルサイズが増えてしまうことが挙げられます。例えば、Web ページを表示するために必要なリソースのサイズの上限をあらかじめ決めておくことで、それ以上に不要なリソースが入り込んでしまうことを避けることができます。パフォーマンスバジェットは、自らのウェブサイトに設けた、パフォーマンスに関わるさまざまなメトリクスの上限を定め、ウェブサイトを高速に保つために便利な考え方です

HPBC では AMP Valid に近づけることをパフォーマンスバジェットとしています。前述したように AMP Valid かどうかの判断は Google の定めたルールに沿って行われます。代表的なルールとして「AMP ページには任意の JavaScript は含めないこと」、「CSS は 75KB 以内にすること」があります。つまり、AMP Valid を目指すことで自動的に不必要なリソースをなくすことができ、パフォーマンスの低下を起きにくくしています。

それだけでなく、HPBC ではパフォーマンスを維持し続けるために Lighthouse CI を用いてパフォーマンスモニタリングを行なっています。これによって、機能の追加や改修で Lighthouse のスコアが落ちてしまったことに即座に気が付くことができます。このように、リリース時から比較してパフォーマンスが劣化しないように心がけています。
上記のような仕組みを整え、万全を期した状態であると考えていましたが、Chrome の DevTools や PageSpeed Insight の結果は私たちが考えているほど良い結果とはなりませんでした。

現状計測

パフォーマンス改善の始まりは現状の計測から行います。なお、今回の計測に利用している環境は以下のものになります。

OS: macOS Mojave 10.14.6
CPU: 2.5 GHz Intel Core i7
メモリ: 16 GB 2133 MHz LPDDR
ブラウザ: Google Chrome 84.0.4147.135
ブラウザキャッシュ: OFF

次節以降で上記問題の原因究明と具体的な対策について紹介していきます。計測結果を見ると、FP/FCP よりも DCL が先に来ていることがわかります。DOM の解析が終わるまで何も視覚的情報が表示されない状態は、ユーザーにとって良質な体験とは言いづらいと考えられます。HTML の解析が完了するよりも前に、何らかの意味のあるコンテンツが表示される方が好ましいと言えます。

また、DCL が先に完了することは仕方がないとしても、HTML の解析が完了して数百ミリ秒後にようやく何らかの描画が行われていることがわかります(DCL〜FP)。その後、最初に何かが描画されてから、次に意味のあるコンテンツが描画されたと判断されるまでにも、数百ミリ秒の遅延が発生していることがわかりました(FP〜FCP)。これらは描画を遅らせる不必要な処理が入っている可能性があると考えられます。そして、Web ページの中で最もサイズの大きいコンテンツの描画(LCP)にも 2 秒弱の時間がかかっていました。画像にも最適化が必要そうであると予想できます。

問題特定

当記事は、 AMP を用いたフロントエンド開発のパフォーマンス改善に焦点をあて、バックエンド側(API サーバーなど)のパフォーマンスチューニングについては割愛します。問題を特定するためには、Chrome DevTools の Source タブ、Performance タブ、Network タブを利用します。大まかに以下のステップを繰り返して解析を行いました。

①Source タブで問題のありそうなコードを削除
②Performance タブで計測を行う(Elements タブで body タグを消してから計測するとわかりやすい)
③速度が改善された場合はそのコードが何をしているのか Network タブなどを使って調査する

なお、上記のサイクルはできるだけ本番環境のソースコードをそのまま流用するために、Chrome DevTools の Source タブ の Local Overrides の機能を利用して試行しました。

問題解決

問題特定のプロセスによって、改善できそうな問題をいくつか見つけることができました。それらの解決策について「DCL〜FP」「FP〜FCP」「〜LCP」の 3 つに分けて紹介をします。

DCL〜FP

Web Fonts のダウンロードに時間がかかる

HPBC では、Windows 端末向けにデザイン上の観点から Web Fonts を利用していました。Web Fonts は CSS でフォント情報の在り処を定義しておくことで、ブラウザで CSS が読み込まれたら動的にフォント情報をダウンロードします。この時、HPBC ではフォント情報の読み込みまでの間、レンダリングをブロックしてしまっていました。
Web ページで利用したフォントは font-family で指定します。HPBC の font-family は以下でした。

font-family: "arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Hiragino Sans, Noto Sans JP, sans-serif";

font-family では、利用できるフォントがシステム内に存在しなければ後続のフォントを利用し、最終的に全てに合致しない場合にはデフォルトのシステムフォントが利用されます。
今回 HPBC では、メインユーザーであるモバイルユーザー(特に iPhone)にはヒラギノのフォントが採用されるため Web Fonts が利用されていませんでした。それにも関わらず全端末のブラウザでレンダリングブロックを引き起こしてしまっていたため、パフォーマンスとデザインのトレードオフを考えて、パフォーマンスを優先して Web Fonts を削除することにしました。

Service Worker のインストールに時間がかかる

HPBC では、オフライン時でも閲覧できるようにするために Service Worker を利用しています。Service Worker はブラウザからのリクエストとレスポンス内容をプロキシすることで、リソースを端末内部にキャッシュすることができます。それによって、オフライン状態でも一度アクセスしたことがあるページを表示することができます。
しかし、Service Worker は初回起動時にブラウザへのインストールが必要になります。つまり、HPBC にアクセスしたことがないユーザーは初回アクセス時に Service Worker のインストールを待たないとレンダリングを行うことができません。これは Service Worker が全てのリソースをキャッシュしておかないとオフラインで表示することができないため、あらゆるリクエストよりも前にインストールしようと試みるからです。
これはオフラインでも利用できる UX と初回アクセス時のパフォーマンスのトレードオフの関係にあります。今回 HPBC では、オフラインでも利用可能としている機能を優先し Service Worker はこれまで通り利用し続けることにしました。

(※編集注記:記事公開後、Service Workerがインストールされても描画速度の遅延には影響がないことがわかりました。お詫びして訂正いたします。)

FP〜FCP

AMP 用 の JavaScript をダウンロードしないとコンポーネントが描画されない

AMP は Web Components であるため、AMP が用意しているランタイムや拡張の JavaScript が動いてはじめてコンポーネントが描画されます。後述する、amp-optimizer によってサーバサイドであらかじめ描画できるコンポーネントも存在しますが、基本的には JavaScript がないと意味のあるコンポーネントとして描画されません。逆に言うと、AMP 用のランタイムや拡張の JavaScript は AMP を利用する場合には必須のスクリプトになります。そのため、Preload を用いて HTML の解析と同時に事前にリソースを取得する処理を行うことでパフォーマンス改善が期待できます。例えば amp-sidebar というサイドバーのためのAMPコンポーネントを利用する際には以下のように書くことができます。

 

AMP 向けの適切な最適化が行われていない

前述したように Next.js は AMP の HTML 向けの最適化ツールである amp-optimizer を自動的に実行しています。amp-optimizer の機能の1つには、クライアントサイドで描画する AMP のコンポーネントをサーバサイドで描画しておくことで、AMP ページのレンダリングパフォーマンスを最適化するものがあります。ちなみに、AMP Cache から配信される HTML にもこの最適化内部で行われています。
しかし、AMP コンポーネントの中でも、クライアントサイドで描画されるのを待たないとレイアウトが崩れるコンポーネント(※1)が存在しており、これらのコンポーネントを含んだページは全てクライアントサイドで描画するためにJavaScriptの実行を待つ必要があります。具体的には、 amp-optimizer が対象の HTML を解析し、該当するコンポーネントを見つけると amp-boilerplate のスタイルを付与します。これにより、最大 8秒間画面を真っ白 (body の visibility を hidden) にします。AMP ランタイムである v0.js がロードされて、実行されると visibility: hidden が解除されて画面が見えるようになります。参照
これらは、クライアントサイドレンダリングが必要な AMP コンポーネントを配置するのをやめたり、代わりの AMP コンポーネントを使ったりすることで、amp-boilerplate をつけずにサーバサイドレンダリングすることが可能になります。
今回の最適化に関しては記事を執筆中に AMP 側の修正があり、HPBC 側のコードを変更することなく amp-boilerplate がつかない形で描画されるようになったため、今後はこちらの問題は発生しなくなります。

※1 amp-experiment, amp-story, amp-dynamic-css-classesなどが挙げられます。

〜LCP

キービジュアルがプリロードされていない

今回の計測をしたページで最も大きなコンテンツはキービジュアルでした。このキービジュアルは amp-img を用いてレンダリングをしています。画像についても、amp-optimizerを用いた最適化が出来ます。そのオプションとしてpreloadHeroImageがあります。preloadHeroImageは、画像を事前にダウンロード(preload)しておき、かつサーバー側でレンダリングしてあげるためのものです。

最適化後の結果

・web fonts の削除
・Preload の付与
・amp-boilerplate の削除(AMP のサーバサイドレンダリング)
・PreloadHeroImage のオプションをつける

上記の最適化を実施した結果、改善前と比較してパフォーマンスが向上しました。

Web Fonts を削除したことで、FP までの時間が短くなりました。また、事前にリソースを取得することができるようになったことで、FCP が改善したことがわかります。

考察と今後の展望

LCP についてはバイト期間中には完璧な対策をできませんでした。画像のフォーマットを Webp に変更することで改善が可能と思われますが、Webp を表示することができないブラウザも存在しているため一括で Webp に変更することはできません。簡単にブラウザごとの差異を吸収するために picture タグが用意されていますが、こちらは AMP に対応していません。amp-img コンポーネントは fallback 機能も存在していますが、無用なリクエストを発行してしまうことになりこれは有効な策とは言えません。そのため今後は画像向け CDN 側でアクセスしてきたブラウザごとに、動的に適切な画像フォーマットを返すような仕組みを取り入れる必要があると感じました。

まとめと感想

今回 HPBC のパフォーマンス改善に取り組ませていただき、AMP を利用していてもパフォーマンスの改善余地がいくつもあることがわかり、勉強になりました。改善を進める上で大事なのはフロントエンドの知識をベースに持っておくことであり、使っているライブラリが AMP か React かは関係なく、ブラウザが HTML を解釈して描画するまでをきちんと理解して地道に計測と改善を繰返さないとパフォーマンスを改善していけないのだなと、身をもって感じることができました。

HPBCでアルバイトをさせていただいた3ヶ月間では、多くのことを学ぶことができました。特にAMPについては実装をしたことがなく、参画当初は何もわからない状態でした。そこから、機能開発、SEO対応、そして今回記事にさせていただいたパフォーマンス改善の取り組みをさせていただきました。これらを通して、AMPやWEBについての理解が深まったのは勿論、サービスを成長させるために考えないといけないこと、エンジニアサイド/ビジネスサイドを問わずチームとして建設的なディスカッションをしていくことの必要性も学ぶことができたと思っています。