TUNAG管理画面のフロントエンドリアーキテクチャでチャレンジしたこと

https://cdn-ak.f.st-hatena.com/images/fotolife/h/hisayuki_mori/20240424/20240424134821.png

こんにちは、スタメンでTUNAGの機能開発チームに所属している森(@hisayuki_mori)です。

今回、2023年の11月からスタートした新機能開発においてフロントエンド側の開発を担当しました。
この5ヶ月の間に新しくチャレンジしたことの紹介をします!

チャレンジしたことまとめ

  • Node.jsを18系から20系にアップデート
  • Reactを17系から18系にアップデート
  • Storybookの8.xをalpha版から導入
  • Biomeの導入
  • testing-liblraryの導入(v12)とアップデート(v15)

振り返るといろいろなことにチャレンジできたなと思います。
それでは、これらを実現できた背景について話していきたいと思います。

背景

TUNAGにはユーザーが見る画面とは別に、管理者のみが見える画面が存在しています。
以前のブログ記事のリプレイスはTUNAGの中のユーザー画面と言われる箇所のリプレイスであり、管理画面は含まれていませんでした。
そのため、今回ユーザー画面側をリプレイスすることになった背景にある開発効率がよくない課題が管理画面にはそのまま残っていました。
背景についてはこちらの記事を参考にしてください。 tech.stmn.co.jp

今回の新規機能開発では、管理画面の実装ボリュームがそこそこ多かったため、現行のアーキテクチャでの実装を積み上げるのは避けたいと考えました。 せっかくユーザー画面をリプレイスしているので、アーキテクチャを見直すことで古い技術を捨てつつ、社内のスキルセットを合わせていきたいと思っています。

そこで、リプレイスは今はできないですが、今後の新規開発では可能な限りユーザー画面のリプレイス同様の実装に寄せ、切り離しプロジェクトが発足した際に備えたアーキテクチャでの実装を進めることにしました。

以前のブログ記事で、フロントエンドチームがフロントエンドのリプレイスを行っていることが紹介されています。
基本的な技術選定やアーキテクチャはフロントエンドリプレイスに追随しているため、こちらの記事を参照してください。
tech.stmn.co.jp

詳細

ここからは実際にチャレンジしたことの詳細を書いていきます。

Node.jsを18系から20系にアップデート

開発が始まった当初はNode.jsは18系のままでしたが、まずはユーザー画面リプレイスに合わせて20.9.0まで上げました。

管理画面はTUNAGのリポジトリに同居していますが、それぞれのディレクトリごとに独自のpackage.jsonを保持しています。
そのため、各ディレクトリごとにNode.jsのバージョンも指定できます。
大まかなディレクトリ構成は以下の通りです。

├ .storybook
├ app
│  ├ javascript
│     ├ (~~~)
│        ├ 新機能の管理画面 👈 今回の実装を配置する場所
│          
├ frontend
│  ├ 管理画面
│  | ├ package.json 👈 現行管理画面のpackage.json
│
├ biome.json
├ esbuild.config.mjs
├ package.json 👈 package.jsonはrootに配置

今回の新機能の管理画面用のpackage.jsonは、現在の実装の関係でルートに配置する必要があるため、そのようになっています。

新しいディレクトリに実装を始めたため、今回の開発ではどのディレクトリにも属さず現行の制約に縛られることなくNode.jsのバージョンを上げることができました。
開発が落ち着いた現在では、バージョンを20.12.2にまで上げており、20.x系の最新版に対応しています。

Reactを17系から18系にアップデート

ReactはNode.jsとは異なり、ビルド後のJavaScriptファイルがすべてグローバルにあるReactを参照してしまう課題がありました。
そのため、現在の管理画面ではReactReact DOMをCDNから呼び出すように変更しました。
これにより、package.json側のReactは自由にバージョンを変更できるようになりました。

この課題に関しては機能開発チームではなく別のチームに作業を依頼し、解決してもらいました!

Storybookの8.xをalpha版から導入

Storybookを導入するタイミングで、既に 8.0.0-alpha.0 がリリースされていました。  v7の安定版を使用することも考えられましたが、UIコンポーネントを作成する段階であったことや、alpha版であっても最初からv8を導入してv8の仕様に合わせることで、よりスムーズに作業が進められると考えました。
開発が終了する頃には、v8が安定版になっている可能性も考慮しました。

ただし、8.0.0-alpha.6から次のバージョンにアップグレードする際に問題が発生し、一時期バージョンのアップグレードが停滞しました。
その際の原因は次の通りです。 github.com

Stroybookのconfigにpluginとして明示的に@vitejs/plugin-reactを記述する必要になってました。

import type { StorybookConfig } from '@storybook/react-vite';
import { mergeConfig } from 'vite';
import react from "@vitejs/plugin-react";
import tsconfigPaths from 'vite-tsconfig-paths';

const config: StorybookConfig = {
  /** ローカル環境でStorybookを起動するときに使用するViteの設定 */
  async viteFinal(config) {
    return mergeConfig(config, {
      /** ↓ここに`react()`が必要になった **/
      plugins: [react(), tsconfigPaths()],
    });
  },
  docs: {
    autodocs: true,
  },
};
export default config;

解決後には8.0.0-rc.2までバージョンをアップグレードすることができ、その後は定期的に最新版にアップデート出来るようになりました。
現在はDependabotを使用して細かなアップデートを行っています。

StorybookのバージョンがあがるとAddOnのバージョンも上がるため、コマンドで一括で上げてます。

npx storybook@latest upgrade

開発が落ち着いた今では8.0.9まで上げてあります!

Biomeの導入

最初はESLintとPrettierを使っていたのですが、

ESLintとPrettierがともかく遅いからなんとかしたい!!

というモチベーションで、すこしでも早くしようと思って導入しました。
LinterやFormatterはhusky + lint-stagedを使ってコミットのたびに発火するようにしています。
そのため、lintやformatが遅いとコミットのたびにその遅さに付き合わなければなりません。
CI/CDなど、頻繁に発生するプロセスの速度に関しては、このプロジェクトではかなり気にかけたところです。

最初はPrettierをやめてRust製のdprintに変えてみて、これだけでもPrettierより結構早くなったので結構満足してました。
その後にBiomeがメジャーバージョンが上がった事を知って、ESLintと置き換えるために検証したところ・・・

13秒かかっていたLintが0.24秒になりました!!

ESLintに戻ることが難しくなったためLinterはBiomeに移行し、dprintで設定できるフォーマットもBiomeが可能であるため、FormatterもdprintからBiomeに乗り換えました。

Biomeは開発が盛んなため、更新とルールの追加が頻繁に行われています。
なのでバージョン上げたタイミングで新しいルールが追加されてLintで引っかかることはよくありました。

基本は全ルールをOnにしており、後から追加のルールや適用が難しいと思われるルールについてはoffにしてます。

ESLintのルールが充実してきたため、最近は少し落ち着いてきました! biomejs.dev

importの並び替えや未使用importの自動削除など、機能も増えてきたのでオススメです!

testing-liblraryの導入(v12)とアップデート(v15)

管理画面側にはtesting-liblraryがもともと入っていなかったため新しく導入することにしました。

v13以上はReact17非対応であったため最初はv12からしか入れられませんでしたが、Reactの課題を解決していただいたおかげでtesting-liblraryのバージョンも上げることができv14まで上げられました。
その後にv15へのメジャーアップデートが来たので、既に書いてあったテストを一通り流して特に問題がなかったのでそのままv15までアップデートしました。

まだ新規の範囲が小さいのでテストの量も少ないのもあって、そこまで苦戦することもなくアップデートできたのでよかったです。

やりきれなかったこと

完全な切り離しは今の管理画面の仕様では機能実装と並行で行うのは困難で、Ruby on RailsにReactを乗せる形での開発からは完全に抜けることはできなかったです。
そのためNext.jsも入れられませんでしたが、切り離しになった際にNext.jsにスムーズに移行できるような状態には出来たと思います。

また、切り離しのプロジェクトが検討してもらえるところまで持っていけたのは、新しいことの知見以外でチャレンジして得られた成果かなと思います!

最後に

ここまで長々と書きましたが、最後まで読んでくださりありがとうございました。

今回やってみたいことを打診できたことや、自分のチームだけでなく垣根を超えて他チームに助けてもらったことが多々ありました。
その上でなりたったチャレンジだと思っているので、協力をしてくださった方々にはとても感謝してます!

スタメンでは一緒に働いてくださるエンジニアを絶賛募集中です!! プロダクトに興味を持ってくださった方でも、技術に興味を持ってくださった方でも、ぜひ一度下のリンクを覗いてみてください!

herp.careers