Next.jsへの移行をほぼ1週間でやりきる
どうもこんにちは、たくびーです。
弊社ではいくつかのアプリケーションが存在し、それらをモノレポで管理しています。
今回はそのうちの1つのアプリケーションをReact + WebpackからNext.jsへ移行を行いました。
動機
弊社にはいくつかのアプリケーションがあります。ここでは話を簡単にするために以下のようにしたいと思います。
- app1(Next.js Pages Router)
- app2(Next.js Pages Router)
- app3(React + Webpack)
きっかけ
app3以外のアプリケーションはNext.jsで開発されており、知見も多く似たような問題に対しての対応など連携の取りやすい構成でした。
app3も他のアプリケーション同様の構成にすることでモノレポで使用している共通パッケージなどの恩恵に預かりたいというモチベーションがありました。
また、ビルドやルーティング周りを自前で用意していたので「フレームワークで管理するようにしたい」ということが以前から言われていました。
利用する側としてはWebpackの設定が古くツラさがありました。古いゆえに普通のメンバーでは手出しができず"秘伝のタレ"状態でした。
ビルドツールの比較としてViteやRemixなども候補に挙げられましたが、前述の通り他のアプリケーションとの互換性を考えNext.jsに決定しました。
移行に関して
今回の移行に関しては準備を周到に丁寧に移行する方法ではなく、一気に乗り換えエラー箇所を順に潰していくという方法を取りました。
というのも短期集中でガッとやりきる必要があったので、改善に取り組める週を有効活用しやってみようということになりました。
もともとディレクトリ構成やルーティングに関してはReact Routerを使いつつNext.jsの作法をとっていたためそこまで準備の必要がないと判断したためです。
ディレクトリ構成は以下のようになっていて移行前、移行後ともにほとんど変化はありません。
Next.js特有の _app
や _document
が追加されたくらいでしょうか。
src/
└─ pages/
├─ _app.page.tsx
├─ _document.page.tsx
├─ index.page.tsx
├─ 404/
│ └─ index.page.tsx
└─ sites/
├─ new/
└─ [siteId]/
├─ index.page.tsx
└─ example/
├─ index.page.tsx
├─ component.tsx
├─ new/
└─ [id]/
また、今回の移行に際していつものチーム(3人) + フロントエンドリーダー、サポートの5人体制で行いました。
フレームワークの置き換え
まずはapp3のフレームワークをNext.jsに置き換えるところからスタートしました。
とりあえずアプリをローカルで動かせるように以下のファイルを作業を行いました。
- Next.jsをマニュアルインストール
-
next.config.mts
を用意 -
package.json
やtsconfig.json
でビルド周りの整備 - アプリのエントリポイントを
pages/_app.page.tsx
に変更
一旦はこれらの作業だけで pnpm run dev
で動作するようになりました。
アプリが動作しない限りはエラーの特定やどこまで移行ができているのかの把握などが難しくなるので偉大な一歩です。
ルーティングの置き換え
app3の移行前はReact Routerを使用してルーティングの制御を行なっていました。
フレームワークをNext.jsに移行したおかげでルーティングを任せられるようになったので、React Routerで使用していた関数などをNext.jsのものに置き換える必要があります。
ざっくりと以下のような作業をしました。
-
react-router-dom
のLink
をnext/link
に置き換え -
react-router-dom
の フック(useLocation
やuseHistory
) をnext/router
に置き換え - パスパラメーターを
Props
注入から Next.jsのuseRouter().query
を使った方法に置き換え
これでルーティング周りを全てNext.jsに移行することができました。
どれも地道な作業でしたが、すでにNext.jsを使用しているapp1を参考にし進めやすかったです。
特別ハマる作業もなくスムーズに行えたかなと思います。
ここまでで「やることをやれば完了する」という見通しが持てたので、以降の作業も完了までいけるのではと感じていました。
テストが動くように整備する
アプリのコードを置き換え以前とほぼ同じように動くようになりましたが、テストがまだ落ちています。
本当はテストを整備し、エラーがないのか確認しながら進めるのが王道かもしれませんが、今回はルーティングの置き換えによってテストは落ちることが予測できたのでテストの整備を後回しにしました。
app3で行なっているテストは以下の2種類です。
- vitestによるコンポーネントテスト、ユニットテスト
- Storybook + storycap-testrun によるビジュアルリグレッションテスト
弊社のStorybookに関する取り組みは下記を是非ご覧ください。
vitestによるテスト(以降spec)もStorybookもReact RouterやWebpackへの依存が多くかなりの作業が必要になりました。
特にコンポーネントで使用していたPropsによるパスパラメーターの注入によりspec、Storybookともにページコンポーネントは大抵修正が必要になります。
また、Storybookの設定ファイルもNext.jsに合わせた設定などの必要があり、動作させるまでかなりの手間がかかりました。
これらの作業を数人で地道に対応し、spec、Storybookからreact-router-domへの依存を全て無くして、いくつかのテストは落ちるがCIで品質を担保できるところまで達成することができました。
テストが動くようになった後の細かい修正
テストが動くようになったので、あとはテストが失敗している箇所やVRTで差分が出ている箇所を重点的に見ていくことができるようになりました。
ここでは大きく分けて以下のような作業に取り掛かりました。
- 画像読み込みに
next/Image
を使うように変更 - 移行前に設定していたメタ情報などを
pages/_document.page.tsx
に移動 -
webpack.config.js
で設定していた環境変数等をnext.config.mts
に設定する - CSS Modulesを使用している箇所でレイアウトが崩れている箇所を修正
- 落ちているテストの修正
- VRTの差分の修正
ここで行なったものはアプリケーションの動作に関連するものは少ないですが、移行前と移行後で品質の担保を行う上で重要なものが多いです。
実はまだ、specやStorybookでは落ちている箇所をskipして今後のTODOにしている箇所があります。
全体として10件以下でクリティカルではない部分なのでキリのいいところまで行いました。
移行へ向けてインフラの整備
app3の開発環境の動作確認はVercel上でしたが、本番環境は別のデプロイ先でした。
今回の移行に合わせて全ての動作環境をVercelで管理するという副次的な環境の統一化が行われます。
これは弊社のインフラチームと協業で行い現ドメインのまま新しいNext.js版のapp3へと移行されます。
また、他のアプリケーションでは行われていた自動デプロイの設定なども行われるのでかなりの開発体験の改善になります。
Vercelへの移行に関しては以下の記事がとても参考になりました。
今後に向けて
これでフレームワークへの移行が済みNext.jsでの開発が始まりますが、まだやり残しがあります。
これらは業務のメインとして取り掛かれないので、効率よく作業し空き時間に対応していく形になります。
主に以下のような作業が残っています。
- スキップしたテストを復活させる
- 不要なファイル、ライブラリを削除する
- 移行時に見つけたTODOを潰していく
- ESLintのプラグインを追加する
- 他のアプリケーションとのコンポーネントの共通化などを適宜行なっていく
- 移行に伴って変更が必要になったDOM構成を修正する
などなど影響度合は低いですが、確実に負債として残っているので全てを終えるまで移行の完了とは言えないかもしれません。
チームで話し合いどれを優先的に対応していくのか決めて取り掛かっていきたいと思います。
移行を達成して嬉しかったところ
今回の移行で古い構成からNext.jsという新しい構成へとなりました。
弊社ではアプリケーションは1つではなく、複数のアプリケーションを各チームで開発しています。
他のチームからするとapp3は「どこか他のアプリと作法が違う」という抵抗感があったのかもしれませんが、これで改善されたと思います。
また、私個人、チームメンバー含めて下記の点は移行して良かったなと感じる点ではないでしょうか。
- 依存関係が整理された
- ツールが揃った
全体的に他のアプリケーションと構成を同じくしたことでテストや設定ファイル、コードの書き方の統一などが行え横断的にチーム間で助け合える環境を作れたことは良かったです。
まとめ
この移行作業を始める前はチームとして大雑把に見積もって1スプリント(2週間)かそれ以上かかるだろうと予測していましたが、今回取り掛かってみて約1週間で実際の移行まで達成できたことは素直に嬉しかったですし、自信にもなりました。
個人的に前々からNext.jsでの開発を渇望していて、何度か訴えてきた甲斐もありました。
ただ、移行して終わりではなく移行後の開発でバリューを出すことも意識していきたいです。
React → Next.jsへの移行はできましたが、今回のNext.jsのバージョンは14系の最新でPages Routerです。(他のアプリケーションと足並みを揃える目的でこの構成にしています)
リーダーからはNext.jsは最新バージョンに更新し、将来的にはApp Routerへの移行も検討していると話を聞いています。
これからはNext.jsのキャッチアップを改めて積極的に行い、次のバージョンアップ等でもスピーディーに対応できるように知見をつけていきたいです。
長くなりましたが、ここまで読んでいただきありがとうございます。
また機会があればお会いしましょう。
Discussion