Amethyst フロントエンドのリニューアル、進行中です。

JADEの今年のアドベントカレンダー19本目。フロントエンドエンジニアの淺津が進行中のAmethystのV2移行プロジェクトを解説します。Next.js App Routerへの対応や技術スタックの刷新など、リニューアルの背景と具体的な進め方をご紹介。開発ご期待ください。

JADEフロントエンドエンジニアの淺津です。

この記事は、JADE アドベントカレンダー19本目の記事です。


フロントエンドのリニューアルを進めています

今回は、現在進めているAmethystのフロントエンドV2(FEV2)移行プロジェクトについてご紹介します。まだ進行中のプロジェクトのため、詳細が固まっていない部分もありますが、リニューアルの背景と進め方について可能な限り丁寧に説明できたらと思います。

開発中のV2の画面(Search Analytics)

 

リニューアルを決断した理由

既に稼働中で一定の顧客基盤を持つフロントエンドを一から書き直すことには、以下のようなリスクが伴います。

  • 開発リソースの消費
    • 新機能の開発や、既存機能のメンテナンスに費やすべきリソースを、リニューアルに充てることになります。
  • ユーザー体験への影響
    • V1 - V2間の体験の差分により、ユーザーが混乱する可能性があります。V1でできていたことがV2でできない、というリグレッションが起きる可能性も考えられます。
  • 予測不能な問題
    • リニューアル前に発生していた負債を清算(あるいは自己破産)したつもりでV2をリリースしたら、結局V2でも多くの新たな問題を生んでしまうというシナリオも考えられます。

こうしたリスクを予想しつつ、それでも移行を決めた背景は次のとおりです。

 

Next.js App Routerへの移行

Next.js 14で登場したApp Routerは、従来のPages Routerとは大きく異なるパラダイムで扱う必要のある機能です。

公式ドキュメントではApp RouterとPages Routerの共存による段階的な移行方法が紹介されていますが、必要な変更があまりに大きく、実質的には一から書き直すのと同程度の労力が必要だと私は考えています。Pages Routerのサポートは継続されるものの、新機能はApp Router優先で実装されることが予想され、公式も移行を推奨しています。

今後もNext.jsで新規開発を続ける開発者は、「移行を前提として、どのように進めるか」を考えなければならない状況にあると感じています(もちろん、Next.js以外のフレームワークへの移行や、フレームワークに依存しない実装も選択肢の一つです)。

既存コードベースの課題

Pages Router から App Routerへの段階的な移行ではなく、App Routerでの完全な書き直しを選んだ理由が他にもありました。それは、Amethystの既存コードベースが抱える問題です。

Amethystフロントエンドは開発開始から2年半以上が経過しており、その間の試行錯誤の結果が蓄積されています。たとえば、i18n用のJSONの記述方式が2種類存在したり、GraphQLドキュメントの配置場所に一貫性がない(専用ディレクトリに集約するか、コンポーネントと同じ場所に置くか)といった問題があります。

また、長期の開発期間による弊害として、機能間でユーザー体験に差異が生じていました。たとえば、Search AnalyticsとUser Analyticsでは、ビューを保存する時のボタン配置や操作手順に微妙な違いが存在します。

これらの問題に個別に対処することも検討しましたが、「個々の問題への対処にかかる工数の合計が、書き直しと同等かそれ以上になる可能性が高い」と判断して、書き直しを決めました。

プロトタイプ開発の成果

手始めに、V2のプロトタイプを作成してみることにしました。そこで感じたのが、開発が意外とサクサク進むなという手応えです。実装の効率が向上した理由を振り返ると、以下のような点が挙げられそうです。

  • App RouterでサポートされたServer ComponentやServer Actionなど、Reactの新機能が扱いやすい。特にデータフェッチを同期的に記述できる点が魅力的
  • 一度作ったことがある機能を作り直すため、仕様が明確に定まっている
  • 既存のコードを気にせず最適な設計を選択できる

技術スタックの変更による効率化ももちろんありますが、一度作った経験を活かしながら一から書き直せることの方が大きな要因だと考えています。

プロトタイプの最初の一画面をチームにシェアしたときの反応が良く、また開発効率も予想以上に高かったため、本格的に移行を進めていくことにしました。

 

技術構成

技術構成については、昨年のアドベントカレンダーで投稿した「Amethyst のフロントエンド構成」と併せて読んでいただくと、現在の構成がよりイメージしやすくなるかもしれません。

blog.ja.dev

V1から変更したライブラリ群、および継続して採用するライブラリは以下の通りです。なお、インフラ周り(Cloud Run、GitHub Actions等)については大きな変更はありません。

  V1 V2
静的解析・フォーマット ESLint, Prettier Biome
GraphQLクライアント

Apollo Client

GraphQL Code Generator

URQL

gql.tada

一部 GraphQL Code Generator

UI Chakra UI (v2)

shadcn/ui

(Radix UI + Tailwind CSS)

チャート Recharts Recharts
ドラッグ&ドロップ dnd kit 検討中(候補: dnd kit)
i18n next-i18next 検討中(候補:lingui or next-i18next)

 

静的解析・フォーマット

深いこだわりはないですが、速さを正義とみてBiomeを採用しました。V1の時にお世話になっていた一部のESLintプラグイン(eslint-plugin-i18nextのno-literal-stringルールなど)が使えない問題はありますが、アクロバティックなリントをしたくなったらそれは方針の方がおかしいということで。

GraphQLクライアント

gql.tadaの体験はすごいです。クエリを書くだけで、データの型が補完される。コード生成が必要なのは、スキーマに対してだけです。神。

Apollo Clientは非常に優れたライブラリですが、APIがややファットになっていることと、App Router対応がいまだにExperimentalであるとされている点がややネックでした。一方でURQLは、gql.tadaと同じく0no-coが開発元であるので、シナジーに期待が持てる点が魅力でした。

ただし、ローカルにスキーマファイルを配置していて、かつgraphcacheにintrospectionの結果を渡したい場合は、今の所GraphQL Code Generatorを利用する必要があるので、そのようにしています。

UI

UIライブラリについては、V1で使用していたChakra UIからshadcn/uiへの移行を決定しました。これは、Radix UIとTailwind CSSを組み合わせたアプローチにより、より柔軟なカスタマイズとパフォーマンスの向上が期待できるためです。また、shadcn/uiはApp Routerとの相性が良く、Server Componentsをフルに活用できる点も魅力でした。

Chakra UI v3への移行も検討しましたが、リリースが発表されたのが10/22とちょうどV2計画を始める直前で情報が少なかったこと、また、新たに書き直すのであればChakraをあえて選ぶ必要性もさほど強くなかったことから採用を見送りました。

shadcn/uiは、パッケージとして配布されるのではなく、CLIを通じてコンポーネントをダウンロードして使用します(実質的にコピペに近い形式です)。これには、アップデートが自動的に提供されないデメリットがありますが、自身でメンテナンスができ、独自の拡張が容易というメリットもあります。

チャート

チャート(グラフ)は、Amethystのダッシュボードの使いやすさを決定づける重要な要素です。

これは、V1で使用していたRechartsをV2でも継続採用することにしました。shadcn/uiが提供するスタイリング済みのコンポーネントが大きな利点となっています。チャートの視認性を確保しつつ、スタイリングの工数を抑えられるこの組み合わせは、理想的な選択でした。

ドラッグ&ドロップ

ドラッグ&ドロップという操作自体にアクセシビリティの課題があるため、「D&Dがなくても操作できつつ、デスクトップではD&Dで快適に使える」体験を目指しています。

現時点では実装を後回しにしていますが、必要性が生じた際にはV1と同様にdnd kitの採用を検討しています。dnd kitは、頻出するドラッグ&ドロップのパターンがパッケージごとに整理されているため、効率的に実装できます。

i18n

i18nについては実装をまだ開始していないため、現時点では確定していません。next-i18nextの継続利用か、Blueskyでも採用されているlinguiの採用か、検証を進めながら判断する予定です。

linguiの魅力は、コンポーネントファーストで開発を進められる点です。JSXの中でメッセージを定義し、CLIを実行するだけで、それが自動的に翻訳キーとして抽出される仕組みが特に魅力的です。

 

テストについて

V1では、以下の3種類のテストを導入していました。

  • Vitestでのロジックのテスト
  • Storybook上でのコンポーネントテスト
  • E2Eテスト

V2のテスト整備についてはいまだ検討段階です。React Testing LibraryがServer Componentに対応していないことが、意思決定を進める上での制約となっています。当面は、純粋な関数として分離可能なロジック部分に対して、Vitestでのテスト実装から始めていくのが良さそうです。

StorybookとE2Eテストについては、V1での経験から、十分なメンテナンスリソースの確保が難しく、次第に技術的負債となっていった教訓があります。そのため、これらの導入は必要性を見極めながら慎重に判断していく方針です。

 

プロジェクトの進め方

弊社のフロントエンドエンジニアは、現時点で私一人です。限られた開発リソースの中で、確実にV2への移行を成功させるために、以下のような進め方をとっています。

  • 1ヶ月の集中期間
  • Internal Preview α - 一部コア機能に対するドッグフーディング (👈今ここ)
  • Internal Preview β - 全機能に対するドッグフーディング
  • Public Preview β - 実際に利用するユーザーにフィードバックをもらう
  • V2完全移行、V1のクローズ

 

集中期間

V2を本格的に作り始めて1ヶ月は、集中期間としました。

V2用のチケットは、通常のバックログとは独立した形で一覧化できるようにLinear(チケット管理システム)上でプロジェクトを定義しています。機能をサブタスクに分けながら、片っ端からPRを作成し、(レビューは挟まずに)マージしていきます。

Linear上のチケット(ごくごく一部)

他にフロントエンドに強みを持つエンジニアがいる場合はしっかりとしたレビューを挟むべきかと思いますが、今の体制で速さを取るならレビューをスキップするのは致し方ない部分と判断しました(その分、チケットがどんどん閉じていくので気持ちいいですね……。いいことなのかはさておき)。

この期間、V2以外のタスクをあまり触らなくて良いように配慮いただいたチームメンバーには感謝しかありません。

現在

1ヶ月が経過した時点で、完成している範囲をInternal Preview環境にデプロイしました。ここでフィードバックを募りながら、さらに不足分の機能の開発を進めています。

Internal Preview環境でのフィードバック

集中期間は終わりましたが、稼働中のAmethystフロントエンド(V1)に対しては新機能の追加は避けてバグ修正のみを適用しつつ、基本はV2のタスクを触っています。

今後

Linearプロジェクト上のタスク・Internal Previewフィードバックを消化しきった段階で、V1でのユースケースをあらためてリスト化し、機能の不足・リグレッションがないかをテストします。その後、Internal Preview β → Public Preview と進んでいく予定です。

来年1月中には、お客様に試していただける状態にすることを目指しています。

Public Previewで、どういうリアクションが来るかは正直なところ未知数で、不安もあります。既存のV1に慣れているユーザーにとっても、違和感なく使えるV2となることを目標に、フィードバックを収集しながら改善していく予定です。Amethystコミュニティの皆さまのお力をお借りすることになると思いますが、何卒よろしくお願いします。

 

最後に

今回は、フロントエンドV2計画についてご紹介させていただきました。

このリニューアルを通じて、Amethystの使いやすさと機能性をさらに向上させ、より快適な体験を提供できることを楽しみにしています。皆さまに愛されているAmethystならではの良さを大切に守りながら、さらなる進化を目指して開発を進めてまいります。

 

アドベントカレンダーは残り4日です。お楽しみに。

adventar.org