はじめまして。Wantedlyでフロントエンドエンジニアをしている @kobayang です。
先月行われていた、CyberAgent が主催する CyberAgentHack/web-speed-hackathon-2022 に参加しました。参加した結果、Webのパフォーマンスについて意識が高まったので、やったこととか、そこで得られた学びについて書いていこうと思います。
アジェンダ
- Web Speed Hackathon 2022 について
- コンテストの仕組み
- 参加したモチベーション
- コンテスト結果
- 特にスコアが上がった取り組み
- 0→400(基礎部分)
- 400→480(チューニング部分)
- 取り組んで得られた学び・気づき
- Lihghthouseのパフォーマンスに詳しくなった
- モバイルのLighthouseはデスクトップに比べてシビア
- メインスレッドの処理時間を減らすのは難しい
- クライアントの計算量の削減はフロントエンドのホットなトピック
- まとめ
Web Speed Hackathon 2022 とは?
"Web Speed Hackathon 2022" は、非常に重たい Web アプリをチューニングして、いかに高速にするかを競う競技です。
「Web Speed Hackathon」はリモート参加型のハッカソンです。 予め準備してある Web アプリケーションのパフォーマンスを改善することで競い合います。
改善対象となるサービスは、架空の投票サービスでWebのパフォーマンスをスコア化して得点を競うコンテスト形式のハッカソンです。より具体的には、以下のような形式です。
- Lighthouseのパフォーマンスの結果をもとに算出されたスコアを競う
- 11/1〜11/27までの期間開催してた。
ISUCONと比較すると、Lighthouseのスコアを競うことになるので、Webフロントエンドのチューニングが重要になるコンテストになっています。
コンテストの仕組み
1. 計測対象のサイトを用意する
2. Issueにコメントを打つと github action が動き、スコアが表示される
実際の計測Issue
(余談だけど、このコンテストの仕組み面白かった)
参加したモチベーション
- 普段の業務だとプロダクトの機能開発の比重が大きいので知識の偏りを感じていた
- パフォーマンスチューニングの経験量を増やしたい
- 自分でもいけそう(始めた時はスコア100点ぐらいでトップ10に入れた)
コンテスト結果
最終結果は6位でした 🎉🎉
- ボードだとこんな感じで、最終スコアは489点でした。
- ちなみに初期スコアは10点
- 上位陣がほぼ満点ですごい…
スコアが上がった取り組み
- 400点近く(平均80点ライン)までは、heroku onlyで基礎的な改善を行いました。
- 400点以上は、CDNによるキャッシュなどを利用してスコアをブーストしました。
0→400
JSの最適化
- Webpack の mode を production にして minify を有効にする
- バンドルサイズを小さくする
- fontwaresome のアセット化
- moment, lodash を使うのやめる
- core-js 使うのやめる
左が初期値で、右がチューニング後です。左だと zengin-code, fontaresome をはじめ色々なモジュールが入っていますが、右を見ると、react-domが支配的になり、バンドルサイズが削減できていることがわかると思います。
また、よく言われることですが、JSのバンドルサイズを計測するために webpack-bundle-analyzer は必須だと思いました。
画像の最適化
- 画像の描画でなぜか canvas を使っていたのを img に変える
- 画像にサイズを入れてLayout Shiftを抑える
- 画像をWebpに変換する
アプリケーションコードの最適化
- JSのアニメーションをCSSに変える
- サイズの大きなJSを使っていたダイアログを非同期でロードする
- ホーム画像をサーバーから非同期で取得していたのを初回描画時に表示する
上記のような基礎的な改善を行うことで400点近くまで上げることができました。
400→489
300点後半まで行くと(自分の力だと)セルフホスティングで戦うのは難しそうと判断して、Next.jsへ移行することにしました。フロントエンドをVercelに、サーバーサイドは現状のままHerokuにして、アプリとシステムを分離します。
Next.js にしたのはWantedlyのフロントエンド基盤がNext.jsなので、Next.jsでのパフォーマンス向上の知見を貯めたいというねらいもありました。
具体的には以下のようなことを行いました。
- Next.js にする
- Heroku だけだったのを Next.js にした
- Vercel(フロント)と Heroku(サーバー)に切り離した
- これ自体はスコアにそこまで影響はない
- SSR & ISR
- Edge Runtime & Server Component
- app directory でできる Edge Runtime や Server component を有効にした
個人的に気になっていた Next.js の App Directory やその機能である Server Component を試せたのはよかったです。スコア自体はすごく伸びたわけではなかったですが、TTIやTBTなどに効果があることを確認できました。
取り組んで得られた学び
Lihghthouseのパフォーマンスに詳しくなった
c.f. https://zenn.dev/shimabukuromeg/articles/9a9ac2006f0efd
FCP, LCP, Speed Index といった初回描画までの時間を計測するスコアや、TTI, TBTのようなメインスレッド処理の計測に関するスコア、CLSのユーザー体験に関わるスコアなど、このスコアを上げるための施策の手数が広がりました。
モバイルのLighthouseはデスクトップに比べてシビア
このハッカソンをするまで実は知らなかったのですが、Lighthouseのスコアはモバイルの方が低く出ることが多いです。デスクトップで満点を出してもモバイルでは全然スコアが高くない、といったケースもよくあるのだと学びました。
- Lighthouse は Chrome の DevTools から確認することができる。
- Device を Mobile, Desktop で選択できる
Wantedlyのプロフィールページの例を出しておきます。デスクトップで100点近く出たと思っても、モバイルだと70点近くまで落ちてしまっています。
なので、パフォーマンスチューニング時には、モバイルのスコアも確認しようと思いました。
モバイルで満点取るの難しい
続けてですが、モバイルで満点を取るのは非常に難しいと思いました。
- 80点ぐらい(緑ライン)まではセルフホスティングでもなんとかなりそう
- 100点を取るためにはCDNによるキャッシュ、React自体をやめるなどパフォーマンス用に何かしらのチューニングをしないと辿り着けなさそう
逆にいうと、80→100の間に戦略の幅があり、ここに競技性があるなと思いました。
参考記事
- 満点とった人のブログを見ても、最終的にはCDNを使っている
- SSRも自作していてめちゃくちゃ勉強になる記事でした。
- カリカリにチューニングするとセルフホスティングでも満点近く出せる
メインスレッドの処理時間を減らすのは難しい
チューニングを詰めていくと最終的にはTBTやTTIのスコアを上げるのが難しくなっていきます。理由として、LighthouseのモバイルではCPUの処理能力が低いことが想定されているため、単純なO(N)のような処理でも、Nが大きいとそれだけで処理時間が伸びてしまいます。
- 描画までに必要な計算量を減らすのには限界がある
- メインスレッドの処理時間が長くなることでTTI, TBTなどの指標が悪化する
- 解決策として、クライアントでの計算を他に逃すことが重要
- ただしそれはFCPやLCPなど他の指標とトレードオフの関係にあることもある
特に React (with SSR) だと、Hydration によって処理時間が伸びてしまう傾向にあるので、ここをどう抑えるかは一つキーポイントになると思いました。
ref: https://blog.jankoritak.com/so-why-server-components
クライアントの計算量の削減はフロントエンドのホットなトピック
現在進行形で仕様策定や新たなフレームワークが生まれている
- 例) React Server Components: サーバー側であらかじめReactコンポーネントを作る仕様
- 例) Qwik: Resumableという概念を導入したフロントエンドのフレームワーク
CDNまわりも盛り上がってる
- パフォーマンス改善においてはCDNによるキャッシュやEdge Computingは重要。
- ちょうどハッカソン期間中にCloudflare D1が使えるようになったりしてた。
まとめ
パフォーマンスチューニングはユーザー体験&KPI&SEO的に重要です。ハッカソンを通して、モバイルのパフォーマンススコアはデスクトップに比べてシビアであることと、満点に近づけるためにはいろいろな戦略があることを学びました。
また現状では、スコアを突き詰めようとすると現時点ではCDNに手を入れる必要がありそうで、今後もWebパフォーマンス改善はフロントエンドのホットなトピックであり続けるだろうなと思います。
このハッカソンを通して、Webパフォーマンスの改善のための手数や、そもそものパフォーマンスへの関心を高めることができて、良い機会になりました!この機会を提供していただいた、CyberAgent の皆さん、ありがとうございました!
宣伝
今自分がいるチームでは、内部品質の改善に取り組んでいます。そのプロジェクトの1つとして、10年以上使われているユーザー向けの募集ページをNext.js+GraphQLによる新しいフロントエンド基盤に移行する取り組みをしています。将来的には募集以外のレガシーなページも、新しい基盤に移行していきたいと考えています。
今回のパフォーマンス向上のような話も含め、品質の高いWebアプリケーションを作るためにはどうしたら良いか?ということを日々考えて、プロジェクトを進めています。
興味がある方は是非話を聞きにきてみてください!