テラーノベルにおけるNext.jsのApp Routerを利用してみて
こんにちは!テラーノベルでiOS/Android/Webとフロントエンド周りを担当している @kazutoyoです!
先日、「Node学園 42時限目 Next.js AppRouterについて」に参加させていただいて、App Routerについて深く知ることが出来ました。
弊社のサービスであるテラーノベルでも、昨年末あたりから徐々にPage RouterからApp Routerに切り替えています。
今回は弊社でApp RouterをProduction環境に導入したことをお話しようと思います。
App Routerの導入で対応したこと
App Routerの導入にあたって、次のように対応を進めました。
- 既存のPage Routerとは共存させる
- 新規ページ/リデザインなど行うページからApp Routerに置き換えていく方針
- なるべくServer Componentとなるようにする
- Server Componentの恩恵を受けたいので、極力Server Componentに出来る部分はServer Componentとして実装する
- 既存のPage RouterはEmotionを使っており、その点でServer Componentにしづらいためスタイリングライブラリを新たに選定(後述)
- 実装/仕様上難しく、Server Componentで複雑になるような場合はClient ComponentとしてもOK
- Server Componentの恩恵を受けたいので、極力Server Componentに出来る部分はServer Componentとして実装する
- ディレクトリ構造は既存のPage Routerでも行っていたように、Feature-Driven Folder Structureとする
スタイリングライブラリでPanda CSSを採用
Page RouterではEmotion + ChakraUIを使用していましたが、EmotionはClient Componentとして実行をしないといけないため、別のスタイリングライブラリを検討しました。
Page RouterではChakra UIを使用していることもあり、開発元が同じPanda CSSを利用することにしました。
Panda CSSのバージョンも2024/05/10時点で0.39と、まだまだ開発中で不具合等もありますが、それほど大きな問題もなく利用できています。
またArk UIとPark UIも利用して、コンポーネントの開発に役立てています。
React Server Component + SSR Streaming
App Routerを使う上で一番メリットを感じられたのはReact Server Componentでした。
テラーノベルでは、ページのメインコンテンツ以外に関連の作品を表示するなど、複数のデータソースから要素を表示するページ構成となっています。
Page Routerの場合、getServerSidePropsなどでそれらの情報をすべて取得した後にレンダリングが行われるため、TTFBが遅くなります。
App Routerでは、メイン以外の要素をStreaming SSRとすることで、ページが表示されるまでの時間をPage Routerより削減することが出来ます。
このあたりについてはTakepepeさんのこちらの資料が分かりやすいです。
ディレクトリ構成の変更
現在のディレクトリ構成として、次のような配置方法となっています。
.
├── app
│ ├── _components // 共通(Presentational)コンポーネント
│ ├── _containers // 共通コンテナーコンポーネント
│ ├── _hooks // 共通hooks
│ ├── _providers // Provider
│ ├── _repositories // リポジトリ層(データアクセスなど)
│ ├── _types // 型定義
│ ├── globals.css // PandaCSSのimport等
│ ├── layout.tsx // 共通レイアウト
│ ├── s
│ │ └── [storyId] // ページごとのディレクトリ。以下 `_` Prefixのつくプライベートディレクトリはページ内でのみ利用する
│ │ ├── _components
│ │ ├── _containers
│ │ ├── _types
│ │ ├── _utils
│ │ └── page.tsx
│ └── not-found.tsx
├── features // Page Routerで利用する機能単位のディレクトリ構成
│ ├── [featureName]
│ │ ├── components
│ │ └── page // pagesディレクトリに配置するページコンポーネント
├── pages // ページルーターのルーティング
└── utils
Page Routerでは、機能単位のディレクトリ構成としており、 features
ディレクトリに pages
のディレクトリ構造と近い形でコンポーネントやロジックを置いていました。
App RouterではFile Colocation、Private Foldersの機能により、ページファイルと関連するファイルが置けるようになっため、 page.tsx
と同階層に配置するように変更を行っています。
また、今回はApp RouterとPage Routerが共存しますが、App Router用のコンポーネントとPage Router用のコンポーネントなどを分けたかったため、App Routerに関連するファイルはすべて src/app/
配下に配置するようにしました。
キャッシュ周り
テラーノベルのNext.jsはCloud Runにデプロイをしています。
Next.jsでは以前までVercel以外ではキャッシュ周りをうまく扱う方法がありませんでした。
Next.js 14.1でようやくセルフホスティング環境がサポートされ、そちらを簡単に扱えるようになったnext-shared-cacheがコミュニティによって公開されました。
テラーノベルでは現在試験的にRedisにキャッシュするように行っています。
特に問題がなく運用できており、キャッシュヒット率も悪くないといった感じです。
ただ、キャッシュをしたい場面/したくない場面の判断や、time-base Revalidationの最適化など、考えることも多いので、今後はどうしようか悩んでいる状況です。
Node学園42時限目の中でも話されていましたが、現状キャッシュをうまく活用できている事例も多くないため、キャッシュを利用しないのもアリかなと考えております。
App Routerでやらなかったこと
App Routerを導入するにあたって、やらなかったこともいくつかあります。
Page RouterからApp Routerへすべて移行はしていない
Page RouterからApp Routerへ主要なページは移行していますが、アクセス頻度が少なかったり、移行コストの高いページに関してはPage Routerのまま残しています。
こちらは緩やかに移行できればよいかなと考えています。
また、こちらの発表でもありましたが、Page RouterとApp Routerを併用するといくつか不具合を踏む可能性もあるので、こういった点は注意する必要がありそうです。
Server Actions
Web版のテラーノベルはデータの参照系が多いため今回利用する機会がありませんでした。
今後、機会があれば利用していきたいと考えています。
Parallel Routes / Intercepting Routes
Intercepting Routesは利用しようと考えていたのですが、ルーティング周りの挙動が不安定だったため今回は見送りました。
現在Next.jsの修正が行われているようなので、安定してきたら再度トライしてみたいと考えています。
まとめ
App Routerに移行したことで、全体的なパフォーマンスも改善し、File Colocationなどを使うことで開発時の体験も良くなりました。
App Routerはまだまだ安定しているとは言えないですが、次第に良くなっていくのではないかと考えています。
App Routerでは、Server Component、キャッシュ、Server Actions、Parallel Routesなど目新しい機能が多いですが、これらを使うかは利用者に任せられているので、自分にあったものを選んで使うのがよいのではないかと思います。
それでは良いApp Routerライフを!
Discussion