こんにちは! マネーフォワード クラウド横断本部のWebエンジニアの はるやま (@linnefromice) です。
現在クラウド横断本部では BtoB 向けサービスのリプレイスプロジェクトの開発を行っており、その過程で実施した UI ライブラリのバージョンアップを通して学んだことを紹介させていただきたいと思います!(@thinceller)、一緒にこちらの対応方針を考えたり対応をしました!)
背景
現在進行中のプロジェクトは2021年初から着手しており、UIライブラリとして他PJでも利用実績のあった MUI (当時 Material-UI ) を選定しました。 当時の最新バージョンは v4 でしたが、開発を進めるうちに v5 の情報が上がってきました。 v5 のスケジュールを見ると、プロダクトリリース前に v5 が正式バージョンとなることがわかったので、v5 にあげるための活動をスプリントに組み込み、実際にバージョンアップに取り組むことにしました。
参考
取り組んだこと
こちらはバージョンアップ時の実際の Pull Request です。
上記 Pull Request を中心に、実際のバージョンアップに関して取り組んだことを一部紹介していこうと思います。 まず Material-UI に関連するライブラリを最新バージョンにします。
yarn add @material-ui/core@next @emotion/react @emotion/styled yarn add @material-ui/styles@next
その後主に Core components の Breaking changes に合わせた修正を取り入れていきます。 ライブラリのバージョンアップが終わった後は、
- import 先ライブラリの変更
- props name の変更
- MUI Component の構造変更に伴う利用している className の変更対応
- default props の変更対応
など色々あるのですが、多くの変更についてマイグレーションガイドでの説明と変更ツールとして codemod) が準備されています!
参考
ただそれ以外にも多くの変更点があり、上記のガイド, codemod で対応できずに自分で修正が必要なこともありました。こちらは一部ですが例えば、
- Palette のデフォルトカラーの変更
- Box Component に対し、border 有無自体の設定後に borderColor 定義をする
- ↑のようにしないと、borderColor が反映されない
- 参考: [system] borderColor not respected anymore · Issue #16995 · mui-org/material-ui
- Next.js の NextLink と組み合わせた Link コンポーネントでの ref の型エラー
などです、個別の問題ごとに issue を探したり、MUI v5 のドキュメントを見て対応したりしました。 マイグレーションガイドに記載があることもないことも含めて、バージョンアップにあたり影響のある箇所を検知できたのは既に整えていた開発基盤によるものが大きかったです、こちらについては後述で紹介したいと思います。
MUI v5 になってよかったこと
バージョンアップの取り組み自体学びは多かったのですが、実際に追加/改善された機能でも便利なことが多く、少しだけ紹介したいと思います。
sx
props- スタイリングのための props であり、Component に直接渡せる
sx
props というものが追加されました style
のように通常のスタイルの記述に加え、theme も利用できたりと柔軟にフットワーク軽く一部のコンポーネントのスタイルを変更できます- 個別の用途に応じたスタイルのカスタマイズなど使いまわさないスタイル定義に対する利用を推奨しているようです
- 参考: The sx prop - MUI
- スタイリングのための props であり、Component に直接渡せる
Unstyled components
- スタイルと UI コンポーネントごとのAPIを分離するための仕組みです
- 最近聞く headless component をイメージすると良いと思います
- 参考: Unstyled components - MUI
- スタイルと UI コンポーネントごとのAPIを分離するための仕組みです
- 新規コンポーネント
- Stack, Pagination, Rating, Data Grid, Date Picker ...
などなど書ききれないくらい多くの追加/変更があります。Unstyled components
など残念ながらまだ活用できていないものもありますが、プロジェクトでも sx
props や Stack
コンポーネントなどはよく利用しています!
参考
やっておいてよかったこと
先述の通りマイグレーションガイドに記載があることもないことも対応したことは多かったのですが、これらを検知/対応するために、既にやっていてよかった、と思うことがいくつかありました!
React Testing Library を利用した DOM nodes に対するテスト
フロントエンドに対するテストとして React Testing Library を利用したテストコード実装をUI実装とともに行っていました。なるべく role attribute を指定した DOM のテストを組んでいましたが、MUI v5 により既存コンポーネントの作りが変わったことを検知してくれました。
ex.
... const menuitems = screen.findAllByRole('menuitem'); expect( within(menuitems[0]).getByRole('heading', { name: /2020\/12\/25/ }) ).toBeInTheDocument(); ...
テスト実装の話ですが、特にテスト対象コンポーネントからさらにテスト対象のDOMをなるべくwithin で絞っていくようにすると、テスト対象をより正確に捉えることができてオススメです。
beta 版でのバージョンアップ
MUI v5 のリリースは2021年9月だったのですが、本プロジェクトでは先行して当時 beta 版だった v5-beta へのバージョンアップを2021年6月に実施しました。正式な v5 へのバージョンアップ対応のコストを下げる目的で、alpha 版を経て大分品質の高い状態で beta 版が出ているのもあり、beta 版へのバージョンアップをしました。 (稼働中プロジェクトだと気軽には行えないかもしれませんが、)実際本リリースされた際への対応コストは非常に下がったため、可能であれば beta 版自体への対応もオススメです。
やっておけばよかったこと
反対にやっておけばよかったなーと思うこともいくつかありました。
ビジュアルリグレッションテスト
例えばデフォルトカラーの変更、variant
props の default value 変更などによるスタイルの変化については Storybook Component を目で確認して気づいたこともありました。
スタイルの変化は React Testing Library ではキャッチが難しい( jest-dom#tohavestyle を使えばキャッチできるのですが、直接 RGB Color Code を記述するため維持が大変です )ため、reg-suitなどを利用したビジュアルリグレッションテストがあった方がよかったのかなと思いました。
MUI Component をラップしたコンポーネント化
当時プロダクトのデザインを実現するために、MUI Component の大幅なカスタマイズが必要なことがなかったため、個別のページに MUI Component を直接利用してスタイル調整のための実装をすることが少なくありませんでした。そのため、バージョンアップをする際に、一部の変更対象 Component / props に対し、横断的に漏れなく調査して同じ修正を入れるという対応をやらざるを経なくなってしまいました。 こういう横断的な調査/横展開的な対応を減らすために、
- プロダクトデザインから抽出した共通 Component の切り出し
- MUI Component を利用した共通コンポーネントの実装
を丁寧に対応しておけばよかったです。 MUI Component をベースにした共通コンポーネントを作成し、その共通コンポーネントを各種ページで利用しておけばバージョンアップ時に対応するのは基本的に共通コンポーネントのみで済むので、こういった事前の対応でバージョンアップのコストを削減できたなと思いました。
例えば大きくカスタマイズする必要のない Tooltip コンポーネントでも以下のように切り出したりしました。
ex.
import { Tooltip as MuiTooltip, tooltipClasses } from '@mui/material'; import type { TooltipProps as MuiTooltipProps } from '@mui/material'; import { styled } from '@mui/material/styles'; // ref: https://next.material-ui.com/components/tooltips/#customization const StyledTooltip = styled(({ className, ...props }: MuiTooltipProps) => ( <MuiTooltip {...props} classes={{ popper: className }} /> ))(({ theme }) => ({ [`& .${tooltipClasses.tooltip}`]: { background: theme.palette.text.primary, padding: 8, borderRadius: 6, }, [`& .${tooltipClasses.arrow}`]: { color: theme.palette.text.primary, }, })); export type TooltipProps = Omit<MuiTooltipProps, 'classes'>; export const Tooltip: React.VFC<TooltipProps> = ({ title, children, arrow = true, ...props }) => { return ( <StyledTooltip title={title} arrow={arrow} {...props}> {children} </StyledTooltip> ); };
いくつか"やっておけばよかった"と思うことを挙げさせていただきましたが、他にもオススメの事前対応等あれば教えてください!
まとめと感想
MUI に限らないと思いますが、ライブラリバージョンアップ自体は多くがマイグレーションガイドがあるので想像以上に楽にできました。バージョンアップによる追加/改善機能を使えるだけでなく、バージョンアップの経験を通してそのライブラリ自体のより better な利用方法がわかったりすることもメリットだと思いました! また今回 beta の段階からバージョンアップをしたので少し対処を探した部分もありますが、多くが他の人も同じような課題にぶつかっていてその問題に該当する issue が既に上がったりしていて、暫定対応方法がすぐ見つかったりします。
UIフレームワークのバージョンアップにおいては、特に今回記述させていただいたやっておいてよかったこと/やっておけばよかったことを既に対応しておくととても楽だし、享受出来るメリットも大きいです! バージョンアップはまだ難しいという方は、まずここにあるやっておいてよかったこと/やっておけばよかったことの一歩が踏み出せると良いかなと思うので、是非取り組んでみてはいかがでしょうか。
最後に宣伝です🙇♂️ クラウド横断本部では、こういう改善も含めながら社内基盤サービスの改善や技術的負債に取り組んでおり、これらを一緒に推進していく仲間を募集しております...!
【サーバサイドエンジニア】_東京(田町) | 株式会社マネーフォワード 【フロントエンジニア】_東京(田町) | 株式会社マネーフォワード
最後までお読みいただきありがとうございました。
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【サイトのご案内】 ■マネーフォワード採用サイト ■Wantedly ■京都開発拠点
【プロダクトのご紹介】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android
■ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』
■だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』