LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

転職会議のフロントエンドパフォーマンス改善〜React/Next.jsでWeb Vitals健全化、スコア2.5倍改善を達成した手法の全て

これは Livesense Advent Calendar 2022 DAY 15 の記事です。

はじめに

転職会議事業部エンジニアの、池田、犬島、佐藤、浜田です。

転職会議は、ユーザーの口コミ投稿によって成り立っている転職サイトです。 フロントエンドはReact/Next.jsによる独立したマイクロサービスとして切り出されており、BFFを通じてバックエンドのサービス群とAPI通信する構成をとっています。

サービスの性質上口コミを中心とした検索流入が重要であり、SEOの文脈でフロントエンドのパフォーマンス改善の必要性は認識されていましたが、機能開発や負債解消もあり、まとまった対応ができない状況が続いていました。

この状況を改善すべく、1年間継続的に改善に取り組み、Web Vitalsの健全化、パフォーマンススコアの大幅な改善を達成することができました。

本記事では、特に効果が大きかった改善策を中心に、私達のフロントエンドパフォーマンス改善の取り組みをご紹介します。

Web Vitalsの不良を撲滅、スコアを2.5倍に改善

まずは、私達のフロントエンドパフォーマンス改善によってどのくらい数値的な改善があったのかをご覧ください。

Web Vitals

Google Search Consoleのウェブに関する主な指標(モバイル)において、後述するWeb Vitalsの【不良】を7万件から0件、【改善が必要】を1.84万件から1.04万件に削減しました。

改善前 改善後

パフォーマンススコア

PageSpeed InsightsというGoogleが提供しているモバイル端末やデスクトップ端末における実際的なサイトパフォーマンスに関するレポートやページの改善方法を提供してくれるウェブサービスを利用してスコアを計測しました。

転職会議で最も利用されている「企業口コミページ(モバイル端末)」のパフォーマンススコアにおいて、改善前は25というスコアだったのですが、改善後は62(約2.5倍)というスコアまで改善しました。

改善前 改善後

Core Web Vitalsとは?

GoogleがWebサイトのパフォーマンスを定量化する指標として2020年5月に発表したのが、Core Web Vitals(コアウェブバイタル)です。 Googleが良いユーザー体験を提供するために重要な以下3つの項目をガイドライン化したものです。

指標 説明
LCP(Largest Contentful Paint) ページで中心となるコンテンツの読み込み完了時間
FID(First Input Delay) ユーザーが入力可能になるまでの遅延時間
CLS(Cumulative Layout Shift) 読み込み時のコンテンツのがたつきの発生度合い

すでにCore Web VitalsはGoogleの検索順位におけるパフォーマンス評価に用いられているため、この指標を改善することがSEOに良い影響を与えます。 転職会議でもこの指標をいろいろな手段を用いて改善を試みました。

改善策の紹介

私たちが行った改善策を順に紹介していきます。その中で効果が大きかった改善策には👌をつけています。

LCP

  • 画像のインライン埋め込み無効化によるHTMLサイズの削減(👌)
  • APIレスポンスのgzip圧縮
  • CDNの圧縮方式をBrotli対応
  • react-chartjs-2 バージョンアップ

CLS

  • レイアウトシフトの改善(👌)
  • 画像読み込みのnext/image化

FID

  • 広告遅延読み込み
  • 第三者スクリプト読み込みの改善

LCP

画像のインライン埋め込み無効化によるHTMLサイズの削減

転職会議では、1つの口コミコンポーネントに複数のアイコンを表示しており埋め込みの対象になる画像の数が多い状態でした。そのため、画像のインライン埋め込みにより一部のページではHTMLサイズが1.3MBまで肥大化していました。 画像のインライン埋め込みはリクエスト数の減少を期待できますが、base64のテキストデータに変換することで、HTMLのデータサイズが増加してしまう問題があります。

実施内容

画像のインライン埋め込みを無効化しました。

結果

大量のsvgがインライン埋め込みされていたためHTMLサイズが1.3MBと肥大化していましたが、インライン埋め込みを無効化することで330KBまで減少しました。 HTMLが小さくなったことでページ表示までの速度が早くなり、LCPが5.7秒から1.2秒に改善されました。

改善前 改善後

APIレスポンスをgzip圧縮する

フロントエンドからバックエンドにはAPI経由でデータを取得していますが、PageSpeed Insightsでレスポンスを圧縮することを推奨する旨の警告が表示されていました。

PageSpeed Insightsの警告

実施内容

フロントエンドから通信するBFF層はRailsで構成されているため、Railsのミドルウェアでgzip圧縮を行いました。

結果

第三者スクリプトについてはgzip圧縮できないため、スコア上の大幅な改善影響は認められませんでしたが、自サイトのAPIについては警告が解消されたことを確認できました。

CDNの圧縮方式をBrotliに対応

Brotliとはオープンソースのデータ圧縮アルゴリズムで、多くの場合はgzipよりも高い圧縮率を提供してくれます。 転職会議ではIE11をサポート外にしているため、アクセスするほとんどのブラウザでBrotli圧縮に対応している状況でした。

実施内容

転職会議の静的アセットはCloudFrontから配信しており、設定を追加するだけで比較的容易にBrotliに対応することができました。

結果

配信する静的アセットのサイズを削減し、LCPの改善を期待していました。 しかし、JavaScriptの圧縮後のサイズはgzipと数キロバイトしか差が出ず、期待していた効果は得られませんでした。

CloudFrontのbrotli圧縮レベルがどの程度なのかはわかりませんが、そもそもボトルネックにはなっていないため、スコアに良い影響は出ない結果となってしまいました。

react-chartjs-2 バージョンアップ

転職会議ではグラフの描画にreact-chartjs-2を採用しています。webpack-bundle-analyzerでバンドルサイズの内訳を調査した結果、chart.jsが不要な機能も含めて全てimportされていました。さらに依存するmoment.jsが意図せず読み込まれていることが判明し、バンドルサイズの肥大化とメインスレッド処理への影響が確認できました。

実施内容

当時の採用バージョンはv2でしたが、v4で導入されたtree shaking対応で不要な機能の読み込みを除外できることが判明したため、アップデートを実施しました。

結果

webpack-bundle-analyzerでchart.js、moment.jsが占めていた割合の削減が確認できました。

改善前 改善後

バンドルサイズの削減によりメインスレッド処理が軽減され、LCP、FCP中心に、TTI、TBTも改善し、ページによってはパフォーマンススコアが10程度改善しました。

改善前 改善後

CLS

レイアウトシフトの改善

レイアウトシフトの対策では、クローラーにレイアウトシフト発生と判定されている箇所を特定することが重要になります。以下の流れで対象箇所を特定しました*1

  1. サーチコンソールでCLSが発生しているページを把握。
  2. PageSpeed Insights、またはChrome DevToolのlighthouseから、該当ページでCLSが発生している箇所(コンポーネント)を特定。

  1. Chrome DevToolsから、More Tools - Rendering - Layout Shift Regionsを有効にし、レイアウトシフト発生のタイミング、詳細箇所を特定。

※ ページロード直後、アコーディオンが閉じた状態から開くタイミングでレイアウトシフト発生と判定され、青色でマークされています。

実施内容

レイアウトシフトが発生している箇所の高さの初期値を固定しました。

結果

冒頭のサーチコンソールのキャプチャで示している通り、サイト全体でCLSのエラーが解消されました。

レイアウトシフトは、発生箇所が特定できれば修正は比較的容易で、改善効果が高いため、優先的に対応することをお勧めします。

画像読み込みのnext/image化

Next.jsには画像の配信向けにnext/imageコンポーネントが提供されており、サイズや形式の最適化など、ほぼ自動でパフォーマンス周りの調整が適用されます。 Next.jsを採用する場合画像には極力next/imageを使用することが望ましいですが、一部適用できていない箇所が残っていました。

実施内容

一部の適用できていない箇所をnext/image化しました。

結果

スコアの大幅な改善は認められませんでしたが、next/imageを使用する場合は画像のサイズを事前に指定する必要があり、これによって該当の画像で発生していたCLSの警告を解消することができました。

FID

広告遅延読み込み

転職会議では広告枠を導入していますが、広告枠の入札や広告自体の読み込みでFIDに影響を与えていることが判明したため、広告の遅延読み込みによる改善を試みました。

広告のタグにはGoogle Publisher Tag(以降GPT)を使用しています。GPTの遅延読み込みの設定*2は有効にしていましたが、ファーストビューに広告枠が存在し、ビューポートに入ったタイミングで広告の読み込みが行われるため、実質遅延読み込みが機能していない状況でした。また、GPT自体のサイズも無視できない大きさになっていました。

実施内容

GPTも含めて広告枠を遅延読み込みするよう、スクロールイベントの発生タイミングで動的に読み込むアプローチを取りました。

結果

TTI、TBTが全体的に改善し、効果のあるページではパフォーマンススコアが10ポイント程度改善しました。

改善前 改善後

なお、本改善策はパフォーマンス改善においては有効ですが、収益面に影響が発生するため、プロダクトオーナー、ビジネスサイドを交えて協議を行い、合意を得た上で実施しています。

第三者スクリプト読み込みの改善

第三者スクリプトとは広告や計測などで利用するためにサードパーティベンダが提供し、任意のサイトに直接埋め込むことができるスクリプトのことを指します。これらのスクリプトはサイトに様々な便益をもたらしてくれる一方で、大量に利用するとページの読み込みに時間がかかります。また、DOMの構築をブロックし、レンダリング速度が遅くなることがあります。 転職会議でも様々な第三者スクリプトをGoogle Tag Managerを通して利用しており、PageSpeed Insightsでこれらのスクリプトによってメインスレッドを阻害している指摘がありました。

実施内容

Google Tag Manager経由でサイトに埋め込んでいる第三者スクリプトの要不要をプロダクトオーナーやプロダクトマネージャーと共に整理しました。

結果

残念ながらパフォーマンススコアに影響を与えることはできませんでした。しかし、不要な第三者スクリプト読み込みを減らすことで少しではありますが、ユーザー体験を向上させられたと考えております。

今後の見通し

今回の継続的なフロントエンドパフォーマンス改善によって、LCP、CLSは良好の判定になるまで改善することができました。特にCLSについては改善策の紹介でも述べた通り、発生箇所の特定ができれば比較的簡易に修正できるため、優先的に取り組むことがおすすめです。

FIDについてはユーザー環境では概ね良好な結果となっていますが、ラボデータではFIDのスコアにまだまだ改善の余地が残されているので、今後も改善に取り組んでいきたいと考えています。

React/Next.jsにはクライアントサイドでロードするJavaScriptのサイズが大きいことや、ロードしたJavaScriptのHydrateが完了するまでインタラクティブな状態にならないという課題があり、FIDを増加させる主な要因になっています。React18/Next.js13ではその課題に対するいくつかの実験的なアプローチが提示されているので、有効に活用してFIDの改善に取り組みたいと考えています。

また、FID改善策として今回は利用できなかったのですが、第三者スクリプト読み込みを改善するためにWeb Workerの利用も検討したいと考えています。

転職会議事業部は、パフォーマンス改善のようなエンジニア中心で行う技術寄りの改善策を事業部として後押しする環境があり、技術的チャレンジが行いやすい環境です。 React/Next.jsを利用したフロントエンド開発、パフォーマンス改善に興味のある仲間を募集しています。

hrmos.co