助太刀Web版のフロントエンド開発戦略 - Pages RouterからApp Routerへの移行篇
はじめに
助太刀のフロントエンドチームでは、主にNext.jsを用いて開発しています。
Next.jsのPages RouterかApp Routerのどちらで開発をするか、というテーマでの議論は話題に事欠かないかと思います。そんな中で、私たちは最も開発頻度の高い助太刀Web版をPages RouterからApp Routerへ移行しました。
今回はそこで得た知見についてご紹介します🦋
背景
助太刀はスマホアプリを中心に、建設業界の職人さんをターゲットとしてシェアを拡大してきました。 その後、法人のお客様からご要望があった経緯により助太刀Web版が立ち上がりました。したがって、運用当初はパソコン利用のみを想定していました。
そこから月日が経ち助太刀をご利用いただく機会が順調に拡大したことで、誰でも簡単に助太刀というサービスをご利用いただくために、スマホブラウザからも閲覧できるようにするプロジェクトが始まりました。
(無事に終了し、現在は助太刀Web版としてリリースされています💛)
このプロジェクトを完遂するために一番考慮すべき点は、半年間という短期間で全ての画面をレスポンシブ対応し、認可方法の変更をしなければならず、いかに開発効率を向上させるかでした。
Pages Routerのディレクトリ構成では、 認証前後のlayoutを分けたり、 メッセージ画面をレスポンシブ対応する際に工夫する必要がある為、工数が嵩むことが予想されました。
また今後さらに助太刀Web版の開発が活発になることも予見されました。
機能やリポジトリが複雑化する前段階で、新しい技術を取り入れやすい状態を作り、Next.jsが推奨する技術に追従する方がメリットが大きいと判断したことで、Pages RouterからApp Routerへの移行を決定しました。
開発効率をあげる工夫として、デザインシステム導入も実施しました。この件はまた別の記事に詳しくまとめたいと思います!
Page RouterからApp Routerへの移行手順
基本的にはNext.jsが公式に出している、ドキュメントを参考にしました。
具体的な手順は以下の通りです。
1. `_app.tsx`, `_document.tsx` を `layout.tsx` に変更・統合しルートディレクトリに移動
2. 各ページを `app` ディレクトリに移動
3. getLayout(*1)を`layout` ページに変更
4. 404を `notFound` ページに変更
5. ページのコンテナコンポーネント先頭に `'use client'`の付与
6. `next/head` をmetadata APIに置き換え
7. Router Hooksの置き換え
8. データfetch系の置き換え
9. API routeをRoute handlerに置き換え
特に、Next.js App Routerへの移行で頭を悩ませたのがルーティング周りの設計でした。
Pages Routerでは、useRouter
のevents
をフックすることで、ページ遷移のライフサイクルを細かく制御できました。例えば、「フォーム入力中の意図しない離脱を防ぐ為に、確認ダイアログを表示する」といったUXを、シンプルに実装できたのです。
しかしApp Routerではevents
が廃止されたことで、同様の処理を自前で実装する必要があり、window.onbeforeunload
などを駆使して、ブラウザのライフサイクルを直接制御する方法を取りました。
*1: https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#per-page-layouts
App Router移行後の構成
ディレクトリ構成
現在のディレクトリ構成は以下の通りです。
appディレクトリ下では、認証完了前後でlayoutを分けています。
また、feature単位でディレクトリを分け、その中にapi, components, const, enum, hooks, types, utilsなどのディレクトリを配置しています。
.
└── src
├── api
├── app
│ ├── (auth)
│ │ ├── (singleColumn)
│ │ │ ├── ...
│ │ │ └── page01
│ │ ├── (twoColumns)
│ │ │ ├── ...
│ │ │ └── page02
│ │ └── ...
│ │ └── ...pages
│ ├── (unauth)
│ │ └── auth
│ │ ├── ...
│ │ └── pageZ
│ └── page01
│ └── page
├── components
│ ├── app
│ │ ├── header
│ │ └── footer
│ ├── logo
│ └── ui
├── const
├── features
│ ├── ...
│ └── feature01
│ ├── api
│ │ └── common
│ ├── components
│ ├── const
│ ├── enum
│ ├── hooks
│ ├── types
│ │ └── ui
│ └── utils
├── hooks
│ ├── app
│ ├── form
│ └── utils
├── lib
│ ├── abac
│ │ └── policies
│ ├── chakra-ui
│ │ └── theme
│ │ └── components
│ └── googlemaps
│ ├── api
│ ├── hooks
│ └── utils
├── resources
│ └── css
├── schemas
├── stores
├── tracking
├── types
└── utils
App Router特有の機能で利用したもの
App Routerに移行して、特に便利だと感じた機能を2つ紹介します。
1つ目は冒頭で述べた通り、layout.jsを用いたレイアウトの共通化です。
認証前後や、その配下のページに対して共通のレイアウトを適用することでコードの重複を避けることができました。
2つ目は、メッセージ画面でのParallel Routesを利用したレイアウトの分割です。
この画面はPCとSPでデザインが大きく異なり、PC画面では画面の左側にメッセージ一覧が、右側にそのトーク内容を表示しています。一方のSP画面は、メッセージ一覧画面をクリック後にそのトーク内容が表示されるUXとなっています。このデザインとパスの遷移の整合性を取る為に、3つのparallel routesを用意することで、メッセージの一覧とそれぞれのトーク画面を同じコンポーネントを使い回し、かつパスの遷移もスムーズに実行できています。
移行の結果
App Routerに移行して認証前後でディレクトリ構成を分けられるようになり、アプリ全体の構造がすっきりしました。
おかげで、新しい構成にも比較的早く慣れることができ、Webアプリのリリース後に必要となった機能追加もスムーズに進められています。
一方で、デプロイはVercelを採用しているのですが、そのうちFunction Invocationsの増加が顕著に表れてしまっています。移行スピードを優先したため、現時点では最適化しきれていない部分もあるので、今後も改善を進めていく予定です!
最後に
助太刀では一緒に開発してくれるメンバーを募集してます!
少しでもご興味を持っていただけたら下記よりお気軽にご連絡ください!
Discussion