本エントリはカケハシ Part 2 Advent Calendar 2023の13日目の記事です。 (Part 1もおもしろい記事がいっぱいあるので、ぜひご覧ください。)
はじめに
こんにちは。カケハシでソフトウェアエンジニアをしている平松です。
今年、新規プロダクト立ち上げの機会があり、その際に行ったフロントエンドの技術選定について紹介したいと思います。
フロントエンドの領域は選択肢が豊富で、変化のスピードも速いため、プロダクトの要件に適した技術を選ぶことはひとつの挑戦です。 実際、フロントエンド技術選定のヒント 【令和五年度版】のアドベントカレンダー記事を読んで、その難しさを改めて感じました。
今回の新規プロダクトは、ユーザがログインして利用するtoBの業務システムです。 私はカケハシでは2度目の新規プロダクト立ち上げですが、前回の経験を活かしつつ、新しいアプローチにも挑戦しています。
この記事では以下を紹介したいと思います。
- 技術選定
- テストについて
- モノレポ、パッケージ分割について
技術選定
早速ですが、選定した技術を紹介します。(テストについては次項に記載します)
SPAアーキテクチャ
- ベース: React + TypeScript
- カケハシでは、Angular、Reactのプロダクトがそれぞれありますが、最近は大きな理由がない限りReactを選定しています
- AngularのプロダクトをReact(Next.js)にリプレイスしました!という記事があるのでぜひ
- カケハシでは、Angular、Reactのプロダクトがそれぞれありますが、最近は大きな理由がない限りReactを選定しています
- ビルドツール: Vite
- 最初はNext.jsやRemixといったフレームワークの選択肢も検討しました。しかし、最終的にはフレームワークを採用せず、ビルドツールであるViteを採用することに決めました。これは私たちのプロダクトにおいては、CSR(クライアントサイドレンダリング)で十分であると判断したためです。
- また、Viteを選んだもうひとつの理由は、そのシンプルさとスピードです。Viteはモダンな開発ツールであり、開発プロセスの高速化を実現します。プロダクトの構成をシンプルに保ちつつ、効率的な開発を目指す私たちのニーズにはぴったり合っていました。
- ルーティング: React Router
- React Router単体ではナビゲーションを型安全にできません。そのため別のライブラリの検討もしましたが、Mobile Factoryさんの以下の記事を参考にTS 4.9で導入されたsatisfies operatorを利用して、型安全なナビゲーションを自前実装できたため、React Routerを採用しました。
- APIクライアント: urql
- バックエンドとのインターフェイスとしてGraphQLを採用しています
- GraphQLクライアントとしてApollo Clientの採用実績がありましたが、Suspense対応していることを理由にurqlを選定しました
- 注: Apollo Clientも3.8からSuspense対応しています(技術選定時はSuspense未対応でした)
- GraphQLの強力なエコシステムの恩恵を受けるため、GraphQL Code Generatorをフル活用しています
- GraphQLスキーマからさまざまなものを生成しています(tsukiji.graphqlというイベントでLTしました)
- フォーム: React Hook Form
- 他プロダクトでも採用実績があり、大きな不満もなかったため採用しました
- スキーマバリデーションのzodと組み合わせて利用しています
- UIコンポーネント: Mantine
- スピード優先ということで、UIコンポーネントライブラリを使うことにしました
- Chakra UIの採用実績がありましたが、後発のMantineを選択しました。MantineはComboBoxやDatePicker周りのコンポーネントも充実している点は大きなメリットでした。
その他
- Linter: ESLint
- Formatter: dprint
- 立ち上げ当初はPrettierを採用していましたが、9月にチームにJoinしたメンバーがdprintに移行してくれました
- 参考: コードフォーマッターをPrettierからdprintにしたら10倍以上速くなった話🚀
- モノレポ: pnpm workspace + Turborepo
- 後述するパッケージ分割の設計方針を取るため、モノレポ構成を採用しました
テストについて
チームのテスト文化の重要性
以前のプロダクト立ち上げで、「後からテストを書く」というアプローチを採用し、その結果としてコードベースが拡大するにつれてテスト追加のコストが上昇してしまいました。この経験から、テストに対するアプローチの重要性を再認識しました。
チーム全体でテストを書く文化が根付くか否かは、初期設計段階での方針決定が鍵となります。 コードが小さいうちから方針を示しておくことで、どのようにテストを書くべきかについての共通認識が形成されます。 前例をコミットしておけば、テストに不慣れなメンバーでも、前例を参考にある程度のテストが書けるようになります。 この点に関して、フロントエンド開発のためのテスト入門の著者の意見に共感しました。
テストの方針と具体的な実装
新規プロダクトにおける基本的なテスト方針として、Testing Trophyを選択しました。これは、フロントエンド開発におけるテストの進め方に関する考え方です。
以下は、具体的なテストに関する技術とツールの概要です。
- モック: Mock Service Worker(MSW)
- GraphQL APIリクエストのモックにMock Service Worker(MSW)を利用し、ローカル開発、コンポーネントのインテグレーションテスト、Storybook、ブラウザ操作テストなどで活用しています
- コンポーネントカタログ: Storybook
- Visual Regression Testing: reg-suit + storycap
- テストランナー: Jest
- コンポーネントテスト: React Testing Library
- E2Eテスト: Playwright
MSWの幅広い活用
新規プロダクトでは、MSWを広範に活用しています。 MSWは、Storybook、Jest、Playwrightと組み合わせて、さまざま状況で効果的に利用されています。
以下の点がMSWの気に入っているところです。
- ネットワークレイヤーでモックするため、より本物に近い形でテストできる
- ローカル開発、テスト、Storybookで、同じ記法でモックを書くことができる
このように大変便利なMSWですが、単純に利用するだけではモックデータの用意と管理が大変です。 そこで、私たちは自動生成を活用しています。 GraphQLスキーマから次のようなコードを自動生成することで、効率的にそして型安全にモックを扱っています。
- query/mutation operationに対応するMSWモックハンドラー
- @graphql-codegen/typescript-mswを利用しています
- GraphQLモックデータ生成用factory関数
カケハシの別のチームの話ですがこちらも合わせてどうぞ。
モノレポ、パッケージ分割について
新規プロダクトでは、pnpm workspaceとTurborepoによるモノレポ構成を導入しています。 pnpm workspaceは複数のパッケージを管理し、依存関係を効果的に管理します。 Turborepoは、モノレポ内の各パッケージに対するタスク実行を簡便にし、開発プロセスを迅速かつ効率的にします。
総じて、pnpm workspaceとTurborepoの組み合わせは、モノレポでの開発を素早く効率的にします。 共通の依存関係管理とタスク実行の最適化により、大規模プロジェクトでも優れた開発体験が得られます。
また、今回のプロダクトでは、パッケージ分割を積極的に利用する方針をとっています。 背景として、前回のプロダクトで、ディレクトリ間の依存関係に制約を入れるため、ESLintなどの設定を入れていたのですが、メンテナンスコストが大きいなと感じていました。 モジュールごとにパッケージ分割し、package.jsonのdependencies/devDependenciesで依存関係を管理することで、設定がシンプルになりました。
以下に、モノレポで管理しているフロントエンドに関係するパッケージ一覧を抜粋してきました。
# モノレポルート ├── apps │ └── frontend # SPAアプリケーションパッケージ └── packages ├── bff │ └── faker # BFF GraphQLモックデータ用factory関数パッケージ ├── branded-type # Branded Typeパッケージ ├── e2e # E2Eテスト(Playwright)パッケージ ├── eslint-config # ベースのESLint設定パッケージ ├── frontend │ ├── auth # 認証関連パッケージ │ ├── features # 各機能パッケージ │ ├── graphql # GraphQL関連(client-preset)パッケージ │ ├── mock # MSWモックハンドラーパッケージ │ ├── test-utils # テストユーティリティパッケージ │ ├── ui # 共通UIコンポーネントパッケージ │ ├── user # ユーザ関連パッケージ │ └── utils # ユーティリティ関数パッケージ └── tsconfig # ベースのtsconfig設定パッケージ
各パッケージの詳細な説明は省きますが、apps, packages配下を見れば、フロントエンドアプリケーションの構成がざっくり把握できるのは大きなメリットかなと思います。
(実際にはバックエンドサービスのパッケージもモノレポで一緒に管理しています)
おわりに
新しく立ち上げた業務システムSPAのフロントエンドについて、簡単に紹介しました。 他にもおもしろいチャレンジをしているので別記事で紹介していければと思います。
明日は同じチームメンバーの種岡さんの記事です。お楽しみに!
最後まで読んでいただきありがとうございました。