キカガク プラットフォームブログ

株式会社キカガクのプラットフォームブログです。エンジニアやデザイナー、プロダクトマネージャーなどが記事を書いています。

キカガク for Business のフロントエンドパフォーマンス向上への取り組みについて

こんにちは、キカガクソフトウェアエンジニアの石橋です。

今回はキカガク for Business でのフロントエンドパフォーマンス向上への取り組みに関して共有したいと思います。

キカガク for Business は主にアプリの画面構成を2つに分けることができます。

  • 学習画面( 受講者が e-Learning を視聴する画面 )

  • 管理者画面( 企業様や法人様の管理者が受講者の学習進捗を確認できる画面 )

パフォーマンス改善については上記の2画面でそれぞれ別の課題があり、取り組みを行っていますので紹介していきます。

学習画面での課題

学習画面はざっくり説明すると下記のような構成になっております。

  • 動画
    • Firestore から Vimeo の ID を参照して iframe を埋め込み
  • 資料
    • Cloud Storage から HTML を取得して表示
  • テスト
    • Cloud Storage から Json を取得して表示

様々なリソースにアクセスをして学習画面を表示する必要があり下記のような課題が生まれていました。

  • 課題1
    • 静的なコンテンツがキャッシュできておらず、画面に戻る際も再びリクエストが発生し都度リソースへアクセスしてしまっている
  • 課題2

このような課題を解決するために、SWR の API である useSWRImmutable を学習画面でも取り入れ対策を行いました。

useSWRImmutable の特徴は公式ドキュメントによると、下記のような記載があります。

データがキャッシュされると、二度とリクエストされなくなります。

一番オーソドックスな SWR の API である useSWR だと options で制御しない限りコンポーネントのマウント/アンマウントで再検証が走ってしまい、キャッシュがあるにも関わらずリソースへリクエストが送られてしまいます。

今回取得対象である HTML や JSON はリアルタイムで変更されることのない静的なコンテンツだったため useSWRImmutable を採用しました。

その結果、もともと抱えていた課題は下記の通り解消することができました。

  • 複数コンポーネントから同じタイミングで同じリソースへのリクエストを複数回送ったとしても、リクエストの回数を 1 度に抑えることができた
  • Next.js の Link コンポーネントでのページ遷移や、コンポーネントのマウント/アンマウント時にも再検証が走ることなく直ぐにキャッシュとしてデータを返すことができ、コンテンツの表示スピードが早くなった

また、現在はさらなるパフォーマンス向上を目指し CDN 化への対応も進めており、近日中にリリースができる予定です。

管理者画面での課題

管理者画面では、受講者の情報を一覧で閲覧することができます。

企業様によっては受講者数が数千人単位の場合もあり、受講者数が多いほど画面上でのレンダリングによる負荷は見過ごせないものとなっていました。

レンダリング負荷に対するアプローチとして、以前より useMemo や useCallback を適切に使いコンポーネントや関数のメモ化を行っていました。

しかし、受講者数の増加に伴いさらに別の視点からのアプローチが必要となってきたためここでは実際に対応した内容を 2 つ紹介します。

1. Chakra UI でアニメーションを含むコンポーネントを使わない

キカガク for Business ではスタイリングに Chakra UI を採用しています。

Chakra UI はキレイで動きのある UI をお手軽に実装できるのですが、一部の Chakra UI のコンポーネントレンダリングへ悪影響を与えていました。

例えば MenuButton コンポーネントです。

一見してパフォーマンスにそこまで影響のなさそうなコンポーネントに見えるのですが、実は MenuButton コンポーネントは下記のような挙動をしています。

  • ボタンを押すまで選択肢は非表示であるにも関わらず、最初から裏では選択肢がレンダリングされている

キカガク for Business では MenuButton を企業内での部署切り替え選択に使っていました。企業様ごとに部署数は異なっており、部署数が数十部署であればこれでも問題がありません。

ただ企業様によっては部署数が数百を超える場合もあり、ここまで多いとかなり初期レンダリングに悪影響を与えます。

また、ボタンを押した後にアニメーションで選択肢が現れるのですが、こちらのアニメーションもかなりレンダリング負荷の高い動作になっており MenuButton コンポーネントそのものを使わず、自前で同じようなコンポーネントを構築する判断としました。

他にも Skeleton コンポーネントも場合によっては、良くない影響を与えています。

例えば Skelton ローディング中に取得したデータに対して JavaScript で重めのスクリプト計算を行います。

すると、Skelton のふわふわしたアニメーションの影響で、スクリプト計算用のブラウザのスレッドリソースが減ってしまいスクリプトの実行時間も伸びてしまいました。

そのため、重めなスクリプト処理が必要なページでは Skelton コンポーネントは使用せず、ローディングアイコンに置き換え、対応を行いました。

Chakra UI は手軽に使える反面、使い所を見誤ると思わぬところでパフォーマンスのボトルネックになってしまうということが学べました。

2. 高負荷なレンダリングは実行タイミングをずらす

管理者画面では、受講者を一覧で表示するためレンダリング負荷が高くなりがちです。

そのため、例えば受講者情報の更新を行うと更新確認用モーダルがカクついて閉じてしまったりと UX が落ちてしまっていた状態でした。

キカガク for Business では昨年に React 18 へのアップデートを行っており、この問題は React 18 から登場した useTransition という hooks を用いて解消をしました。

useTransition の詳しい挙動や仕様は公式ドキュメントに記載があるので割愛しますが、useTransition を使うことで指定した state のレンダリングを遅延させることができます。

つまり、更新優先度の高い state は先に高速で画面上にレンダリングを行い、更新優先度の低い state は他の state のレンダリングが落ち着いたタイミングでレンダリングを行います。

これにより、確認用モーダルを閉じるアニメーションなどユーザー操作により発生する優先度の高いレンダリングは先に行い UX を損なわさせず、更新後のデータはその後にレンダリングさせるということを実現することができました。

これまでも React ではレンダリングそのものを抑える useMemo や useCallback が存在していましたが、レンダリングに優先順位をつけられる useTransition はとても画期的なものだなと実感しました。

おわりに

キカガク for Business はおかげさまで想像を超えるスピードで、導入企業様や受講者が増加しています。

そのため日々、パフォーマンス問題と向き合いながら開発を進めておりフロントエンドエンジニアとしてとても面白いフェーズにあります。

パフォーマンスチューニングに挑戦してみたい!…という方は、是非キカガクの採用ページへどうぞ!まずはカジュアル面談で、ざっくばらんにお話しましょう。