SmartHR Tech Blog

スマートエイチアール開発者ブログ

プロダクト間共通の React コンポーネントライブラリを運用する話

f:id:nabeliwo:20190801151024p:plain

こんにちは、 フロントエンドエンジニアの @nabeliwo です。

弊社には SmartHR というプロダクトの他に SmartHR の従業員 DB を利用して開発・提供される SmartHR Plus アプリ (以下、 Plus アプリ)というものがあります。

SmartHR CTOが語る中長期戦略。徹底的なアプリ開発とAPI対応で「プラットフォーム化」促進へ - SmartHR ガイド

既に多くの Plus アプリがリリースされており、そのほとんどのプロダクトのフロントエンドは React x Redux という技術スタックで構成されています。

Plus アプリは毎回新規でプロジェクトを立ち上げて開発していくことになります。
とはいえブランディングの観点から見ると、基本的なトンマナや UI パーツは SmartHR 本体や、各 Plus アプリ間で揃えるべきという前提があります。
結果として、各 Plus アプリで同じような React コンポーネントやスタイルの実装をすることになります。

そこで共通するコンポーネントをライブラリとして切り出して、各 Plus アプリから呼び出して使えばいいのでは?という考えに至りました。
そして SmartHR-UI が誕生しました。

SmartHR-UI はパブリックなリポジトリとして開発しており、既に npm でも公開されています
この記事では SmartHR-UI をどのように開発・運用しているか、という話をします。

SmartHR-UI の特徴

Material-UI の実装を参考にした React コンポーネント集

React アプリケーションへの組み込みやカラーテーマなどの変更のインターフェースは Material-UI の実装を参考にしました。
これによって、特定の色やサイズを変えたい場合はコンポーネントを使用する側から一括で変更ができるようになっています。また、それぞれのコンポーネントは style や className を props として公開することで直接スタイルを上書きできるので、柔軟に対応できるようになっています。

TypeScript で実装

現在弊社のプロダクトのほとんどがフロントエンドは TypeScript で実装されています。
SmartHR-UI も TypeScript で実装することで堅牢性が増すだけでなく、使用する側のエディタで補完が効いてドキュメントを見なくても勘である程度書けるので、効率良く開発できます。

スタイルは Styled Components を使用

このようなコンポーネントライブラリにおけるよくあるテーマだと思いますが、スタイルの当て方をどうするかは悩みました。
CSS を JS と分離して記述する場合は使用する側に別で CSS を HTML に読ませるか、 webpackcss-loader を使うかを強いることになるし、 style props を使う場合は SmartHR-UI 自体のスタイルの記述が煩雑になります。
これはどのやり方を選んでも一長一短あるかと思います。

今回は、弊社の他の React アプリケーションでは全て Styled Components を使用していることから、 SmartHR-UI の import 以外の手間がかからないという点で、 Styled Components を選択しました。
The State of CSS 2019 によると Styled Components が CSS-in-JS のツールとして最も認知されている、という点も選択した一つの理由です。

f:id:nabeliwo:20190801151658p:plain

https://2019.stateofcss.com/technologies/css-in-js/#tools-section-overview

Storybook でカタログ化

実装としては React コンポーネントを export しているだけなので画面が存在しないので Storybook でコンポーネントを確認しつつ実装しています。
また運用の話の部分で詳述しますが、常に master branch の Storybook を Netlify でホストしているので、いつでも最新のコンポーネントの見た目を確認できます。

現在の master branch の Storybook

SmartHR-UI の運用

共通コンポーネント集を作る上で、そのコンポーネントの粒度や見た目を確定するのはエンジニアの独断でできるものではありません。 SmartHR の各プロダクトや全体のブランディングに責任を持つデザイナの力が必要になります。

デザイングループでも、デザインガイドラインやデザインコンポーネントというものがあり、エンジニアが React コンポーネントでやろうとしていることを Sketch で実現しようとしています。
そのデザインコンポーネントと SmartHR-UI のコンポーネントが一致している状態が理想状態になります。

現状デザインコンポーネントも SmartHR-UI もまだまだ過渡期で、理想状態からは程遠いため、どちらも日々更新されている状態です。
デザインコンポーネントに変更が入った場合や、そもそも SmartHR-UI のコンポーネントの見た目が間違っていることに気づいた場合、デザイナが Issue を立てます。それを受けてフロントエンドエンジニアが対応して PR を出します。
デザイナがそのまま自分で PR を出してくれたりもします。(強い)

あとは通常通りレビュー、マージ、リリースが行われます。
リリース( npm への publish )のツールとして Standard Version を使用しているので、コマンド一つで publish やタグ付けや CHANGELOG の作成が行われます。

依存ライブラリのアップデートに関しては、 Dependabot を使用して weekly で PR が飛んでくるようにしています。 CHANGELOG を見つつマージして、 CI が落ちた場合は調査して修正した上でマージします。

CI の話

SmartHR-UI に PR を出すと、 CircleCI で以下のタスクが動きます。

  • ESLint による lint
  • Jest によるユニットテスト
  • reg-suit による画像回帰テスト
  • Storybook をビルドして Netlify でホスト

ESLint による lint

まずは linter を走らせます。
ESLint の設定も eslint-config-smarthr として共通化しており、各プロダクトは .eslintrc.js で extends するだけで導入が完了します。

設定の内容としては React, TypeScript, Prettier が導入されているプロジェクトを前提としたものになっています。

Jest によるユニットテスト

コンポーネントのテストはまだあまり充実していません。
現状は特に複雑なことはしていないのでスナップショットテスト + α くらいで留まっています。ただ、コンポーネントのテストをあまり厳密にやっても UI の変更に対応する負荷が高いわりに旨味が少ないので、これ以上厳密にしようとは考えていません。

reg-suit による画像回帰テスト

スナップショットテストによってコンポーネントの構造が意図せず変わってしまうことを検知できるのですが、コンポーネントの修正によって他のコンポーネントの見た目が意図せず変わってしまうことは検知できません。

そのため、画像回帰テストを行うことで全てのコンポーネントの変化を確認しています。

f:id:nabeliwo:20190801151454p:plain

reg-suit はこんな感じでコンポーネントの差分を可視化してくれるのでめっちゃわかりやすいです!
画像回帰テストの結果は S3 にホストしていて、 reg-suit が PR に結果をコメントで残してくれます。

f:id:nabeliwo:20190801151641p:plain

Storybook をビルドして Netlify でホスト

Storybook をビルドして Netlify にホストします。
PR ごとに Storybook を作るので、レビュワーは PR の環境をローカルに立てずに画面の確認ができて便利です。

f:id:nabeliwo:20190801151443p:plain

抱えている課題

現状まだ解決できていない問題がいくつかあります。

  • conventional commit messages のルールが徹底されない
  • fork したリポジトリからの PR で reg-suit が失敗する
  • 型が複雑

conventional commit messages のルールが徹底されない

リリースのツールとして standard-version を使っていることは先述しましたが、このツールでは semver におけるどれを上げるかは、コミットコメントによって判断します。
そのため、 conventional commit messages をルールとして設けているのですが、自分含めまだ徹底されておらず、適切でないバージョンアップになってしまったり自動生成される CHANGELOG が適切にならないという状態になっています。

これはコミットメッセージの linter を導入してシステム的に解決する、などやりようはいくらでもあるかとは思いますが、まだ手を回せていないところです。
また Dependabot から送られるコミットのコメントもデフォルトだと conventional commit messages 的には正しくないので設定を変更する必要があります。

正直この程度の問題であればこの記事を書いている時間で解決できることなのですが、現状を正しく伝えたくて書いてみました!

fork したリポジトリからの PR で reg-suit が失敗する

基本的に開発時はリポジトリを fork してから clone するかと思います。
しかしオリジナルのリポジトリの別ブランチからの PR では正常に通る CI が fork したリポジトリからだと失敗してしまうという事態になりました。

原因は CircleCI の環境変数にありました。
reg-suit の実行結果を S3 にホストするため、 AWS のクレデンシャルを reg-suit に渡す必要があり、 CircleCI に環境変数を通して渡していたのですが、 CircleCI はセキュリティ保護のために fork したリポジトリの実行環境では環境変数を渡さない仕様になっていました。

もちろん設定で渡すようにはできるのですが、悪意のある開発者が PR を出すことでクレデンシャルを盗むことができるのでそれは問題外です。

現在行なっている対策として、 PR が飛んできたらまず CI で reg-suit は落として、レビュワーがコードを確認してセキュリティリスクがない場合は PR のブランチを upstream にプッシュすることで再度 CI を回す、ということをやっています。
スクリプトを使うことで1コマンドでできるようにはなっていますが、それにしてもフローが煩雑です。
なので実際の運用としては内部のエンジニアの場合は upstream にブランチを直プッシュして PR を立てています。上記の運用は外部のエンジニアからの PR の場合に限っています。

参考 : Triggering trusted CI jobs on untrusted forks

ここらへんの問題は CircleCI Artifacts を使えば解決できそうな気配は感じているのですが、まだ手が回っておらず今は運用でカバーしているという状態です。

型が複雑

運用の部分で書きましたが、 React を書けるデザイナが自ら PR を出して改善をしてくれています。
フロントエンドエンジニアにとってこれはとても嬉しいことなのでどんどんこの文化が広がって欲しいと思う一方、現状のコンポーネントは TypeScript の型が複雑になっていて、パッと見で難解な印象を与えてしまうのでせっかくコードを触ってくれようとしている方に対して心苦しさがあります・・・。

というのも、使用する側からテーマを変更できるように、 Context API を使ってテーマを埋め込んでスタイルの色やサイズなどの値を抽象化しているのですが、これを解決するための型が複雑で、テーマを反映したコンポーネントを作る敷居が高くなってしまっています。
将来的な構想として外部の開発者にも、 SmartHR の公開 API と SmartHR-UI を使ってアプリケーションを作ってもらえるようなプラットフォームを作っていきたいので、 SmartHR-UI の敷居は下げたいです。

しかし現状ここに関しては具体的にどうしていくべきかという解が見えていないので、これからも考えていかなければならないところです。

フロントエンドエンジニア募集してます!!!!

ここまで長々と、プロダクト間共通の React コンポーネントライブラリである SmartHR-UI の開発・運用の話をしました。
SmartHR のフロントエンドエンジニアはまだ5人と少ないですが、リソースが少ない中でもより良いユーザー体験を届けるべく、技術的な挑戦や業務効率化に取り組みながらスピード感のあるプロダクト開発をしています。

そんな SmartHR で一緒に挑戦をしてくれる仲間を絶賛募集中です!ぜひ!来てください!ぜひ!

フロントエンドエンジニア募集ページ

とりあえず雰囲気を知りたいという方はカジュアル面談も体験入社もありますのでご活用ください🙏
エンジニア向けの体験入社制度ができました

ここまで読んでいただきありがとうございました。