こんにちは!プロダクトエンジニアのshiraです。
2024年6月にリリースした採用管理機能の開発をしています。
support.smarthr.jp
採用管理機能ではNext.jsのApp Routerを採用しています。SmartHRではこれまでApp Routerを使ったプロダクトがない状態だったので、技術選定時は採用するか迷いました。
この記事ではApp Routerを採用するまでの経緯と採用してみてどうだったかについて紹介させていただきます。
Pages RouterとApp Routerについて
まずはじめにPages RouterとApp Routerについて軽く説明します。ご存知の方は読み飛ばしてください。
2024年9月現在、Next.jsのルーティングシステムは2つ用意されています。それがPages RouterとApp Routerです。
Pages RouterはNext.jsが誕生した2016年からあるルーティングシステムです。「pages」ディレクトリ内にディレクトリやファイルを配置するとディレクトリ名やファイル名がURLパスになるという特徴があります。(詳細の説明は割愛しています。)
Next.js 13からはPages Routerに加えApp Routerを選択できるようになりました。Pages Routerは現代のReactの基本的な構造であるストリーミング用に設計されていないため、App Routerが誕生しました。App Routerを使うとServer Components、Streaming with Suspense、Server Actions などのReactの最新機能を使用してアプリケーションを構築することができます。
Pages RouterはNext.jsの新しいバージョンでも引き続きサポートされていますが、React の最新機能を活用するには、新しいApp Routerに移行することが推奨されています。
採用管理機能における前提の紹介
採用管理機能は2024年3月から技術選定および開発を開始し、6月にファーストリリースをするという目標を掲げていました。開発チームのプロダクトエンジニアは3名で、バックエンド寄りのエンジニア2名とフロントエンド寄りのエンジニア1名という構成でした。技術選定開始時の2024年3月時点では社内にApp Routerを採用した事例がない状態でした。
短期間でリリースするという目標自体がチャレンジングであるため、社内で採用事例がないApp Routerを使うことに対して最初は割と消極的な気持ちでした。
しかし、「社内に採用事例がない」という理由だけで決定するつもりはなく、Pages RouterとApp Routerをさまざまな観点から比較し、総合的に判断したいと考えていました。
技術選定の方針
前提を加味して技術選定をする際の方針は以下としました。(ルーティングシステムに限らず、採用管理機能の技術選定全般の方針です。内容は割と当たり前なことなので書くまでもない気はしたのですが、一応紹介します。)
明らかなメリットがある場合、これまでチームメンバーが(ほぼ)使ったことがない技術を採用しても良いが、開発スピードとのバランスを考慮する
使ったことがない技術を採用するケースのイメージとしては、採用のメリットが明らかである上で、以下1つ以上に当てはまるものとしました。
- キャッチアップコストがそこまで高くなさそう
- 世の中や社内である程度知見が溜まってそう
- 採用した際のリスクが低そう
チームで合意が取れていること
当たり前ですが、独りよがりな技術選定にならないようにチームで合意がとれていることを大事にしたいと考えていました。課題があったときはチームで向き合っていくことになるので、その不確実性を受け入れられるかなども合意を取って進めたいと考えていました。
App Routerを採用した経緯
次にApp Routerを採用した経緯について紹介します。
検討フェーズではPages RouterとApp Routerの違いを比較しました。本記事内容に関係する部分だけ抜粋したメリット・デメリットをあげると次のとおりです。
- Pages Router
- メリット
- Next.js誕生時からあるため成熟している
- 社内でよく使われているため社内に知見がある
- デメリット
- React18以降の新機能のうち、一部しかサポートされていない
- 今後積極的に改善はされないと思われる
- 将来 App Router に移行する必要が出てくる可能性がある
- メリット
- App Router
- メリット
- Reactの新しい機能が使える(Reactの新しい機能を積極的に使っていくなら App Router が良い )
- Next.js的にはApp Routerを推奨しており、今後主流はApp Routerになる
- 最初からApp Routerを使っておけば Pages Router から App Router への移行は必要ない
- デメリット
- 社内で採用実績がない(社内に知見がない)
- まだ新しい仕組みのため、できないことやバグが多いかもしれないという不安がある
- メリット
この時点では割とPages Routerを使おうという気持ちが強めでした。理由は主に以下の通りでした。
- 社内事例がないのでなにか詰まったとき大変そう(ここに時間を使いたくない)
- Pages Routerによる知見が社内に溜まっており、アプリケーションの特徴からもPages Routerで事足りると想像できたので、App Router独自の機能を積極的に使いたい理由がいまのところない
- Pages Routerが急に使えなくなることはなさそう
- 約4ヶ月でリリースするためにある程度枯れた技術を使うことで不確実性を減らしたい
自分なりの考えをまとめた後、社内のフロントエンドエンジニアの意見も聞いてみたかったので、週一で開催しているフロントエンドミーティング(フロントエンドエンジニアが集まるミーティング)で意見をもらいました。これまでApp Routerを採用しなかった理由を聞いたところ、他プロダクトで技術選定をした際SmartHR UIが動かなかったという声がありました。これに関しては、私達が開発に着手した2024年3月時点でClient Componentであれば問題なく動くことが確認できました。
それとは別に技術顧問のkoba04さんにも相談させていただいたところ、「App Routerを使うけど全体をClient Componentにする保守的な選択肢もあるよ」というアドバイスを頂きました。*1
社外における課題に直面した事例はどれも新しい機能を積極的に使ったケースだということもあり、保守的な使い方ならリスクが少ないだろうということがわかりました。
何か問題が起きたときはPages Routerに置き換えることもさほど大変ではなさそうという話を社内のエンジニア(前職でApp Routerを使っていた方)やkoba04さんから知見共有いただきました。
以上を踏まえて、撤退ラインを設けつつ保守的な方法で App Router を使うことに決めました。まとめると、採用管理機能チームのエンジニアで出した結論は以下のとおりです。
- App Routerを採用しつつ、全体をClient Componentにして保守的に使う
- Pages Routerに移行したくなった場合すぐ移行できるようなディレクトリ構成にしておく
- 撤退ラインを決め、2週間毎に状況確認をするタイミングを設ける。明らかに解消が難しい課題に直面した場合、定期の確認タイミングを待たずに撤退を判断する
保守的に使用する場合はApp Routerにする意味があるのか?という疑問もあるかと思いますが、メリットとしては以下のようにとらえています。
- 現状はApp Router独自の機能をあまり活かせていないが、将来新しい機能を使いたい時にすぐ使える状態になるので新機能を試しやすい
- Pages Routerを使った場合App Routerへの移行が必要になってくる可能性があるが、はじめからApp Routerを採用していれば将来の移行コストをなくせる
以上の通りエンジニア内での意見を固めたうえで、採用管理機能の開発チーム全体に共有しました。新規プロダクトにおいて社内で採用事例がないApp Routerを使うことや、もしどうしようもない問題に直面した場合移行が必要になることを説明しました。今採用するメリットや保守的に進めること、仮にPages Routerに移行することになった場合にかかりそうな工数を説明した上で合意できたので、App Routerの採用がひとまず決定しました。
技術選定方針として記載した内容と照らし合わせると、以下を満たす状態での採用となりました。
- 採用するメリットが明らか
- キャッチアップコストがそこまで高くなさそう
- 採用した際のリスクが低そう
- チームで合意が取れている
「キャッチアップコストがそこまで高くなさそう」は保守的な使い方をするため、そこまで高くないと判断しました。「採用した際のリスクが低そう」に関しては、保守的な使い方をすることに加え、撤退ラインを決め、問題が起きた場合に移行することも考慮できていたのでリスクが低い状態で進められると考えました。
隔週での振り返り
App Routerで開発を継続できそうかを確認するために定期的にミーティングを設けました。3月上旬に技術選定をしたので、3月末、4月中旬、4月末の3回のタイミングで確認し、4月末時点でそのまま進められそうと判断したのでApp Routerのまま開発を進め、リリースし今に至ります。
隔週で行ったミーティングでは、App Router起因の大きな問題は出ませんでした。チームのエンジニアがNext.jsとApp Routerの両方とも(ほぼ)初心者だったので、基本的なキャッチアップが必要ではありましたが、とても保守的な使い方しかしないと決めていたため、基本的な仕様を理解した後はスムーズに開発が進められたと思います。1点、認証周りだけ割と苦戦して想定より時間がかかりましたが、なんとか乗り越えました。その点は後述します。
App Router を使ってみてどうだった?
最後にApp Routerを使ってみてどうだったかについても紹介します。
今回は幸い、致命的な課題には直面しませんでした。それはとても保守的な使い方をしていることもあると思いますし、現状複雑な要件がないことも影響していると思います。
その中でも少し躓いた点があるので抜粋して2つ紹介します。
認証の実装に手間取った
認証にはNextAuth.jsを使っています。社内の他のプロダクトでも使われており、各プロダクトで共通化できる処理は社内でライブラリ化されています。採用管理機能でも他プロダクトを踏襲して認証処理を実装しようとしましたが、Pages Routerと同様の書き方だとうまく動かない部分があり苦戦しました。NextAuth.jsのドキュメントを見てもよくわからない部分があり、NextAuth.jsのissueを見に行ったりして対応しました。
手間取った原因は App Router だからというだけではなく、認証やNextAuth.js に詳しくないといったことも関係していると思います。詳細は社内ライブラリや社内で採用している認証の仕組みに依存している箇所もあるため、この記事では割愛します。
router.events が使えない
Pages Routerにある router.events がApp Routerにはありません。router.events を使って Router のイベントを監視し、ページ離脱時に通知を消したかったのですが、それができませんでした。
代わりに usePathname と useSearchParams を使って実装しました。(Next公式で提案されている代替手段です。参考:https://nextjs.org/docs/app/api-reference/functions/use-router#examples)
まとめ
この記事では、新規プロダクトでApp Routerを採用するまでの意思決定プロセスと、実際に使用してみた感想について紹介しました。
今回、新規プロダクトを約4ヶ月でリリースするというチャレンジングな目標を立てていました。その中で、保守的な使用方法ではありますが新しい技術の採用にチャレンジできました。これはスピード感とチャンレンジの両立を図れた良い判断だったと感じています。
また、保守的な使い方であれば問題なく運用できるという実績を社内で共有できたことで、他のプロダクトでも採用しやすくなったと考えています。
さらに、最近立ち上がったプロダクトの中には、RSCを活用している例も出てきています。採用管理機能でも、今後はApp RouterやReactの新機能をより活用できるよう検討を進めていきたいと思っています。
We Are Hiring!
SmartHRでは一緒にSmartHRを作りあげていく仲間を募集中です!
少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!
*1:Server ComponentとClient Componentについて
Server Component:
サーバーサイドのみで実行されるコンポーネントです。App Router内で実装されるReactコンポーネントはデフォルトでServer Componentになります。Server ComponentはReact 18 Canary で利用できる機能で、React 19で正式リリースされる新しい機能の一つです。Server Componentはサーバーでしか実行されないため、バックエンドのリソースにアクセスしたり機密情報を扱うことができます。ただし従来の考え方とは全く異なるため、導入の難しさがあります。
Client Component:
Client Component は新しい種類のコンポーネントではなくこれまでのコンポーネントのことです。ブラウザ/サーバー両方で実行されます。App Router内で使用する場合、ファイル冒頭で”use client”を宣言すると、Client Componentとして扱われます。Client ComponentにimportされているコンポーネントもClient Componentとして扱われます。 App Routerを採用しつつ全体をClient Componentにした場合、従来の考え方と大きく変わることはないため、Server Componentを使用する場合に比べて導入が容易になります。