SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

2ヶ月でCore Web Vitals, Lighthouseスコアを大幅改善するために行ったこと

こんにちは!!!スマートキャンプでエンジニアをしている吉永です!

私は現在、スマートキャンプの主力サービスであるBOXIL SaaS(以下、BOXIL)の開発にフロントエンド、バックエンド問わず携わっています。

恐らく新年一発目になる弊社テックブログの記事は私の記事ということで、今年もよろしくお願いいたします。

はじめに

弊社の主力サービスであるBOXILはリリースから時間が経っていることもあり、バックエンド・フロントエンドともにさまざまな技術的負債となる部分を抱えています。

BOXIL開発チームでは、そのような技術負債の返済を積極的に行っており、その一環としてReactの導入や、webpackのビルド時間の大幅な短縮などを行いました。

React、Webpack改善の記事はこちらから

しかし、jQueryやCSSのサイズ問題など、パフォーマンスに大きく関わるが、普段の開発をしながらでは工数の問題や、バグを恐れて容易には手をつけられないものも存在します。

今回の記事では、そんな改善業務の中から去年の秋頃に行った、LighthouseやCore Web Vitals関連のパフォーマンス改善での成果や過程で意識していたことなどについてご紹介します。

なぜパフォーマンス改善を行ったのか

先ほど紹介したBOXILは、ユーザーとベンダーをマッチングさせるプラットフォームというサービスの特性上、SEOの評価が売り上げに大きく影響します。

これまでにも開発チームやメディアチームが主導してSEOの改善やコンテンツの質をあげ、順位を保ってきました。

しかし一時的ではなく継続して効果が出るような対応をしたいと思っても、サービスの規模的に大きな改善をしようとすると、開発メンバーの工数の問題やそれに付随する知識を持ったメンバーが少なくなかなか手をつけられずにいました。

そんな中、昨年8~9月頃、BOXIL開発メンバーに新たに3人のメンバーが加わり若干の余裕ができました。そのため私と新しく加わったエンジニアの永井さんの2人チームで約2ヶ月間BOXILのパフォーマンス改善活動を行なうこととなりました。

Core Web Vitals(以下: CWV), Lighthouseとは?

ここで、先ほどから度々上がっているCWV,Lighthouseについて軽く解説します。

CWVとは

CWVとは、以下の3要素で構成されるGoogleの定めたサイトのUX品質担保のための指標のことを指します。

Googleの検索エンジンでは、各項目の指標に沿ってそのページ内で改善が必要かスコアリングを行い、コンテンツ内容で競っている他のページがある場合、このCWVのスコアも順位の判断基準にします。

各指標の中身は、以下のような内容です。

  • Largest Contentful Paint(以下: LCP): ページの中で一番大きいコンテンツのロード時間を測定する。
  • First Input Delay(以下: FID): ページが読み込まれてからユーザーが最初にアクションを行ったときに、ブラウザが反応するまでの速度。
  • Cumulative Layout Shift(以下: CLS): 視覚的な安定性を測定し、読み込み時に起こるページのレイアウトのズレなどを計測する。

公式ページ

Lighthouseとは

Lighthouseとは、Google製のWebアプリの監査ツールの名称です。

パフォーマンス(CWV, Web Vitals項目)やアクセシビリティなど、Webアプリを作成するうえで重要な5つの項目をどれくらい満たしているのかをスコアリングしてくれます。

Lighthouseのパフォーマンスで計測している項目の説明は以下の通りです。

  • First Contentful Paint(以下: FCP): ユーザーがURLをクリックしてから最初の要素をレンダリングするまでの時間
  • Time To Interactive(以下: TTI): ページが操作可能になるまでの時間
  • Speed Index: ページの読み込み速度
  • Total Blocking Time(以下: TBT): FIDの類似指標、ページ表示からユーザーの操作に応答できるまでの読み込み待ち時間
  • LCP
  • FID

また、Web Vitalsの指標は今後も変更していくとGoogleから告知されています。

そのため、今回はより新しいLighthouseの項目で計測できる可能性が高い、Google Chrome Canaryを使用して各種スコアを計測し、パフォーマンス改善をしています。

CWV & Lighthouseの改善結果

まず、改善結果を発表します。

今回対応したページはBOXIL上で重要なコンテンツと認識しているページすべてにおいてこの対応をしました。

今回は効果がわかりやすく出た、SaaSの情報が記載されているサービスページと、そのレビューが記載されているレビューページを例にあげます。

計測は一貫して以下のページで行いました。

サービスページ

レビューページ

サービスページとレビューページの特徴として、弊社のメインコンテンツである関係上ページ内の要素数はかなり多く、無駄に読み込まれているJavaScriptやCSSによって、パフォーマンスの計測結果に毎度かなりばらつきがありました。

注: 画像でパフォーマンスの数値が違うのは、スコアを測定し直すなどした際にばらついてしまったためです。

サービスページ(改善前)

TBTの計測結果が460msという結果からしても、リソースの読み込みに時間がかかっていることが伺えます。

また、画像にaltがついていなかったことや、widthとheightを明示的に示していないことでレイアウトシフトが起こり、警告され減点対象になっていました。

サービスページ(改善後)

アクセシビリティ、SEO項目がほぼカンストする結果になりました。

また、大きな効果としてはTTIが1/6程に短縮されていることがわかります。

さらに、460msかかっていたTBTが0msになっており、絶大な改善効果を発揮していることがわかります。

これは、後ほど説明するJavaScript削減による効果が大きいです。

レビューページ(改善前)

先ほどのサービスページほど遅くはないですが、やはりTBTがかなり悪さをしていることがわかります。

また、画像が多いページのためaltがついていなかったり、aタグに識別できる属性がついてないことによるアクセシビリティ項目での減点が多々ありました。

レビューページ(改善後)

Best Practice項目以外において、スコアが大幅に上昇しました。

SEO項目に関しては100点になり、さきほどのサービスページ同様JavaScriptの削減効果で410msかかっていたTBTが0msになりました。

パフォーマンス項目は、ページのパフォーマンス改善活動をすると決めてから一番重い課題の一つだと認識していたため、すべて緑色(Good)になったのは素晴らしい結果だと感じています。

以上のように、上記2つのページで大幅なスコアアップを達成し、他サービスページなどでも同様に改善の恩恵を得ることができました。

ここからは、劇的なパフォーマンス改善をするにあたってチームで行ったことや、技術的な改善点、力が及ばずできなかったことなどを発表します。

改善をするにあたってチームで行ったこと

コミュニケーション的なお話

改善を一緒に行ったエンジニアの永井さんは8月に入社したということもあり、私と組んだ時点では、知り合ってまだ1ヶ月ほどでした。

そのため、お互いのスキル感や動きやすい環境などをあまり知らない状態でスタートしています。

まず最初に行ったことは、雑談形式でお互いの経験や、やりたい改善点を話しながら、自分の得意な領域でやれそうなことを開示していきました。

そして、今回の場合だと以下で決定しました。

私の場合。

  • 外部向けの周知施策や依頼の取りまとめ
  • 他社事例を参考にした、施策の実行
  • フロントエンドで完結するタスク全般の消化

永井さんの場合。

  • チーム向けの周知施策やドキュメントの取りまとめ
  • 以前の経験や他社事例を参考にした施策の実行
  • インフラ・バックエンドに関連するタスク全般の消化

これは今後タスクを進めていくうえで、二人のうちのどちらが担当するかを決める重要な指標になりました。

私が外部向けの対応を担った理由としては、BOXILに関わるステークホルダーの幅広さから、入社間もない場合はどこに共有するべきかが迷いそうだからという考えがありました。

そのため、入社して時間が経っている私が担当するほうがロスタイムを少なくやり取りができると思い、引き受けました。

一方の永井さんには、私よりエンジニア歴が長く経験豊富なことから、深ぼって調査する必要がありそうなタスクをお任せしました。

このように、メンバーの性格やスキルを上手く話し合い、お互いの得意領域だけで戦える環境を作ることができたのは後々効果が大きかったです。

また、困ったときに「そういえばあんなこと言ってたな...」と聞きに行くことができたり結果的にお互い円滑にタスクを進めるうえで重要な対話でした。

タスクの洗い出し、調査方法

今回の改善活動は、1ページを早くすれば良かったわけではなく、BOXIL内でも特にSEOが重要な複数ページを対応する必要がありました。

そのため、対応ページの中でも優先度が高いもの、今回であればBOXILのメインの機能となるサービスページから着手することにしました。

また、Lighthouseで指摘される項目はページごとに共通して利用可能なものが多くなることが予想されたので、調査した項目ごとの対応方法をTipsとしてドキュメントにまとめながら作業しました。

また、開発者ツールのNetworkタブで読み込んでいるJavaScriptのサイズを調べた結果、ページの機能からしても明らかに過剰な量を読み込んでいることがわかりました。

パフォーマンスタブでは、ページ全体でどのタイミングでなんのイベントが起きているかを知ることができます。

LayoutShiftなどを対処したい場合、ここでページの画像付きでどう現象が起きているのかを知ることができるためとても重宝しました。

また、レンダリングされてから遅いのか(フロントエンド課題)、レンダリングされる前が遅いのか(インフラ・バックエンド課題)などの切り分けをするためにも使用していました。

そうして洗い出したものはすべてタスクとして積んでいきました。

優先順位付け

積まれたタスクは以下の順番で優先順位をつけることにしました。

  1. 比較的軽いタスク(直すだけの作業タスク)
  2. 重いが効果がありそうなタスク(不要なJS、CSSの削除)
  3. 重いうえに外部との連携が必要そうなタスク(カラーコードなどのデザイン修正)
  4. 重いうえにそもそも対処が難しいタスク(依存してる外部のScriptで指摘されている項目)

比率的には、軽いタスクが書き出したタスクのうちの6~7割ほどを占めていました。

また、予想通りLighthouseから指摘されている項目のほとんどは、いくつかのページで共通していました。

そのため共通した軽いタスクや、改修のコスパが良いものを最初の数週ですべて消化し、スコア上昇が見込めるかやその効果などを知ろうと考えました。

逆に少しでも重いと感じたタスクやコスパが悪そうだと感じたものからは、とりあえず逃げました。

逃げたタスクは後日調査が必要であれば調査タスクにし、時間をかけて対応していきました。

DatadogやSearch Consoleでの定点観測

Datadogでは、主にモバイルページのLighthouseスコアを計測し日々の朝会で確認をしていました。

計測するページは、パフォーマンス対応ページの中でも注力していて、なにか起きたときにクリティカルになりうるページに限定しました。

また、Search Consoleでサービス全体の不良URLの数なども確認し、Sprint Reviewで今週どれくらい減ったのかを全体に向けて発表する場がありました。

どちらも修正が入ったタイミングでスコアが上がっていたり、逆になぜか下がっていたりなどを日々確かめ、何か異常があったときに早期にキャッチすることが目的です。

Sprint Reviewでの報告

BOXILチームでは、毎週金曜日にSprint Reviewという今週の成果発表をする場を設けています。

そこで今週のLighthouseというコーナーを設け、Datadogで定点観測したスコアや上がったスコアを発表しました。初回は用語の説明やなぜやるのか、ご協力のお願いなどを混ぜながら、とにかくチームだけでなくBOXIL運営の組織全体を巻き込み共有をしました。

また、大きな工夫としては自分ではわからなかったことも積極的に報告をしました。

サードパーティ製のものを使用している場合や、管理権限を持つ部署が開発チーム以外だったりするケースも多くあったため、「これ、どうすればいいですかね...?」と言ったような問題も報告していました。

その結果、ある部署の方にその問題をキャッチしていただき、Sprint Review中に700以上の不良ページが改善された事例もありました。

技術的な改善内容

ここからは、上記のタスク内で行った改善内容をいくつか紹介します。

JavaScript、CSSの最適化

タスクを洗い出すタイミングで『開発者ツールのNetworkタブで読み込んでいるJavaScriptのサイズを調べた結果、ページの機能からしても明らかに過剰な量を読み込んでいることがわかりました。』と書きました。

これを詳しく調査したところ、元々はapplication.js or application.cssを読み込む設定になっていました。

これはLighthouseでも「Remove Unused JavaScript(またはCSS)」という項目で指摘されています。

そのため、サービスページ以外のファイルも読み込んでしまい、結果TBTが大きく膨れ上がるという現象が起きていました。

それを踏まえ、JavaScriptやCSSに以下の対応をしました。

def include_javascript_path
    path = "#{controller.controller_name}/#{controller.action_name}.js"
    asset_exists?(path) ? path : 'application'
end

def include_css_path
    path = "#{controller.controller_name}/#{controller.action_name}.css"
    asset_exists?(path) ? path : 'application'
end

private

def asset_exists?(path)
    if Rails.configuration.assets.compile # 動的にコンパイルしているかどうか(dev環境のみtrue)
        Rails.application.precompiled_assets.include?(path)
    else
        Rails.application.assets_manifest.assets[path].present?
    end
end

これまでapplicationを読み込んでいたslimから、該当の記述を削除し、上記のinclude_javascript_pathなどに置き換えました。

BOXILではサービス用のservice_controllerというcontrollerがあり、サービスページを表示するためにはshowというメソッドが呼ばれています。

そのため以下のような名前のファイルを設置し、そのページ内だけで読み込まれているJavaScriptのみを読み込むことで脱却しました。

app/assets/javascripts/services/show.js

ファイルがない場合はもともと読み込まれていたapplicationが読み込まれるので、動作は変わらずそのまま動きます。

修正前は217kbのファイルを読み込んでいましたが、結果として以下のようになりました。

72.0kbにまで減らすことができました。およそ1/3の削減です。

Chromeの開発者ツールからカバレッジなどを確認できるため、それらを参考に何がどれだけ使用されているかなどを確認しながら進めると良いです。

jQueryなどを読み込んでいる場合、かなり圧迫していると思います。

アクセシビリティに関する修正

Lighthouseでは、アクセシビリティに関する問題も報告してくれます。

一番多かったものは、imageタグにalt属性をつけていないことによるエラーでした。

# 悪い例
<img src="test.png">

# 良い例
<img src="test.png" alt="test画像">

alt属性には、画像が表示されなかった際の代替テキストの役割がありますが、スクリーンリーダーが画像情報を読み上げる際にalt属性を指定することによって綺麗に読み上げてくれるというメリットもあります。

ただし、適した画像に適したaltを付けないと、逆に読み上げの邪魔になるケースがあるため、むしろ読み上げてほしくない場合にはalt=""と空で指定できます。

テキストを入れるか入れないかの判断や、入れるテキストの内容は以下の記事を参考にさせていただきながら決定しました。

もうalt属性で迷いたくない

次に、サービス内で使用されているカラーコードの変更です。

BOXIL全体で使用している文字色や背景色の組み合わせによっては、「背景色と前景色には十分なコントラスト比がありません」と指摘されていました。

Lighthouseでのコントラスト比の判定は、Web Content Accessibility Guidelines (WCAG)に則って行われており、少なくとも背景色と文字列には4.5:1以上のコントラスト比がないと指摘されます。

Lighthouseでは以下のように、問題のある点を指摘してくれます。

それを元にBOXILチームのデザイナーに置き換えるまでの手順や方向性を相談し、デザイナーから提案された色に置き換えていきます。

カラーコード修正の手順

置き換える際に、以下の手順で進めていきました。

  1. 置き換える対象の洗い出し
  2. 置き換えるカラーコードをデザイナーと相談
  3. scssファイルに変数を設定
  4. 置き換え開始(今ここ)
  5. 古いカラーコードが定義されている変数を消す

本来であれば古いカラーコードの変数を置き換えるだけで良いと考えていたのですが、色を直接呼び出していたり、別で定義されている変数を呼び出していて、影響範囲が未知数だったりと置き換えの作業が難航しました。

そのため、一気に置き換えようという考えは捨て、いったん自分がすぐ発見した場所を直し、そのほかは見つけ次第どんどん修正していくという運用をすることにしました。

考えた運用方法はドキュメントにまとめながら開発チームに共有しました。

現在は新しい機能を開発する場合は、基本適切なコントラスト比が維持される色を使うことになり、古いカラーコードを呼び出しているところは、定期的な開発改善で置き換えていく予定です。

置き換えが終わったタイミングで古い変数を一掃し、できるだけクリティカルなバグが少なくなるように考えています。

このように、雑多なタスクに加えていくつか大きな変更を伴う開発を行なっていながらパフォーマンスを改善していきました。

その中で、CDNの最適化などにもチャレンジしており、永井さんがその記事を書いているのでぜひ読んでみてください。

Cloudflareの画像最適化料金をWorker KVで97%削減した話

改善活動をするうえで大事なこと

ここからは、改善活動をするうえで特に大切だと感じたことなどを書いていきます。

どうやればいいかが分からない?それはそう。

まず、前提として改善活動をするにあたってどうやってやればいいかが分からないと感じても、それは悪いことではないです。

とにかく、最初は「ああできたらいいよね。こうできたらいいよね」という理想の完成形を挙げ、調査タスクから始めるのも良いです。

調査した結果はドキュメントに残し、改修の手間と効果、改善に使える期日などと相談しながらそのタスクの優先度を決めていきましょう。

改修内容や調査内容のドキュメントは、今後同じ問題に対処しなくてはいけなくなった場合のノウハウ消失が防げますし、自分達がこの一連のプロジェクトで何をしていたかを周りに教える重要な資料になります。

一人でやらない、組織でやる

改善活動をするにあたって、思うように改善結果が出なかったり、逆に大きなバグやこれまでの負債、自分の権限が及ばない部分に行き着いてしまうことがあります。

その時、自身の知識だけでなくサービスのドメイン知識を持ったステークホルダーの協力が必要になります。

BOXILのようにある程度成長したプロダクトでは、そのステークホルダは開発組織だけでなく、マーケ、企画、経営メンバーなど多岐に及ぶため、開発組織を超えて聞かないと分からないような問題も多々ありました。

以下の画像は、自分の権限がなく調査ができなかった際に弊社の社長を直接メンションして聞く私の姿です。

今回、開発組織を超えて改善の協力を要請するにあたって以下のことに気をつけながら進めていました。

改善結果は全員に共有

改善活動をするにあたって、一番懸念していたのは以下のようなことになってしまうことでした。

周囲に自分達が何をしているかをうまく伝え切らずに、「あの人たち、たまにメンションしてくるけど何やってる人たちなんだろう...」となってしまうのはとても勿体無いです。

それを回避するために以下の2点を開発チーム外に向けて積極的に展開し、周知しました。

  • Sprint Reviewで改善施策がどういうものなのかを報告(用語、やること、やった事)
  • リリース時に改善結果をSlackで報告
  • その効果なども一緒に報告

また、助けていただいたメンバーへの積極的な感謝を混ぜることにより、次にまた依頼したときに協力を得やすくなると考えました。

また、Lighthouse計測結果などのBefore / Afterの画像があるとより、結果が目に見える形で共有されるため士気が上がりやすいです。

結果が出たら喜びを共有する

改善活動自体は、タスク単体が地味なものが多く、それを面白いと感じる人はかなり限られていると思います。

さらに、タスクの結果はスコアが上がったとか、CLSがどうたら...とか、なかなかエンジニア以外のメンバーは興味を持ちづらいような内容ではないかと考えています。

そのため、日頃から周囲に自分達のやっていることやその効果をアピールし、その結果が出たらそのサービスに関わる全員で喜べるような空気を作ることが大事です。

周知した結果

周知、結果を出す、喜ぶ、それを周知を繰り返した結果、先月行われた社内の表彰制度で賞をいただきました。

その後嬉しくなるコメントも多数いただき、技術との向き合い方に関しても自信が付く、良い数ヶ月間の取り組みになりました。

ただ、この結果は一人で行なってできたものではなくチーム内外での協力を得られたからこその成果だと認識しています。圧倒的感謝!

今回の記事で書いていた内容は、パフォーマンス改善以外でも自身がやったことを効果的に周囲に伝えるための術を多く書いています。

今後の開発も周りの協力をうまく得ながら進めていくことで、より良い開発体験が得られるのではないかと考えています。

終わりに

パフォーマンス改善は、今日やったから終わりではなく今後も付き合い続けなければいけない問題です。

今後もWeb Vitalsの項目はアップデートしていくと発表があったとおり、SEOと付き合っていく以上長期的な改善施策を組んでいく必要があります。

その改善施策の中には、すぐに結果が出せなかったりそのために必要な工数を割かなければいけない場合があります。

パフォーマンス改善や技術負債の返済に、どのサービスでも共通で必ず改善できる銀の弾丸がない以上、『今できないからやらない』ではなく長期的に、長い目で見ながら、数年後を見据えて少しずつ向き合っていくことが重要です。

この記事が、サービスをよりよく改善するためにパフォーマンス改善を取り入れたいと思い始めた方の助けになれば嬉しいです。

参考資料

改善をすすめるうえで参考にした記事や、最近見つけた事例記事を紹介します。

https://hack.nikkei.com/jobs/Web/hack.nikkei.com

blog.recruit.co.jp

techblog.yahoo.co.jp