Comments
Loading Translate Section...Loading Comment Form...
Loading Comment Form...
このWebサイト、『記事もコメントも自動で翻訳される多言語コミュニティEvame』のメタフレームワークをRemixからNext.jsに移行し、インフラもRenderからVercelへ移行しました。このWebサイト、『記事もコメントも自動で翻訳される多言語コミュニティ Evame』 のメタフレームワークを Remix から Next.js に移行し、インフラも Render から Vercel へ移行しました。
2週間ほどかけての大掛かりな移行でしたが、結果には満足していて、「小〜中規模なサイトならRemix / React Router、大規模なサイトならNext.jsを選ぶ価値が大いにある」という結論に至りました。2週間ほどかけての大掛かりな移行でしたが、結果には満足していて、「小〜中規模なサイトなら Remix / React Router 、大規模なサイトなら Next.js を選ぶ価値が大いにある」という結論に至りました。
この記事では、RemixからNext.jsへの移行についてや、RemixとNext.jsの個人的な比較について書きます。どっちを使うか悩んでいる人や、移行を考えている人はぜひ読んでください!この記事では、 Remix から Next.js への移行についてや、 Remix と Next.js の個人的な比較について書きます。どっちを使うか悩んでいる人や、移行を考えている人はぜひ読んでください!
サイト方針の変更と規模の拡大サイト方針の変更と規模の拡大
当初Evameは、青空文庫やプロジェクト・グーテンベルク、過去の仏典と言ったpubllic domainのテキストに対訳を付与したくて始めました。しかし開発を進めるうちに、public domainのテキストだけではなくユーザが投稿した記事にも対訳を付与できるようにしたいと考えるようになりました。そもそもEvameを作り始めた目的は知識と物語の循環であり、そのためにはユーザーが記事を投稿できるシステムが必要不可欠だと考えたからです。この方針の変更によってサイトの規模が拡大し、必要な機能が増え、大きなエコシステムを持つNext.jsに惹かれるようになりました。当初Evameは、青空文庫やプロジェクト・グーテンベルク、過去の仏典と言ったpubllic domainのテキストに対訳を付与したくて始めました。しかし開発を進めるうちに、public domainのテキストだけではなくユーザが投稿した記事にも対訳を付与できるようにしたいと考えるようになりました。そもそもEvameを作り始めた目的は知識と物語の循環であり、そのためにはユーザーが記事を投稿できるシステムが必要不可欠だと考えたからです。この方針の変更によってサイトの規模が拡大し、必要な機能が増え、大きなエコシステムを持つNext.jsに惹かれるようになりました。
Remixの今後の不明瞭さRemixの今後の不明瞭さ
2024/11/22、Remix v2はReact Router v7とマージされました。https://remix.run/blog/react-router-v72024/11/22、Remix v2はReact Router v7とマージされました。 https://remix.run/blog/react-router-v7
Remixは消え、React Routerとして続いていくのかと思っていましたが、上記ブログの著者でもあるCo-FounderがTwitterで、次はRemix v3になるという発言をしています。Remixは消え、React Routerとして続いていくのかと思っていましたが、上記ブログの著者でもあるCo-FounderがTwitterで、次はRemix v3になるという発言をしています。
https://x.com/mjackson/status/1870288212339634235https://x.com/mjackson/status/1870288212339634235
名前の変更はエコシステム全体に変化を余儀なくさせる大きなものです。実際、別のプロジェクトでRemix v2からReact Router v7へアップデートしたのですが、Remix関連の各種ライブラリが使えなくなり、多くの対応が必要で結構な工数がかかりました。再度名前の変更で同様の対応を迫られる可能性もあり、そうすると今回アップデートしてもまた使えなくなるライブラリが多く出るかも、といった不安がありました。名前の変更はエコシステム全体に変化を余儀なくさせる大きなものです。実際、別のプロジェクトでRemix v2からReact Router v7へアップデートしたのですが、Remix関連の各種ライブラリが使えなくなり、多くの対応が必要で結構な工数がかかりました。再度名前の変更で同様の対応を迫られる可能性もあり、そうすると今回アップデートしてもまた使えなくなるライブラリが多く出るかも、といった不安がありました。
React と Next.js の緊密化React と Next.js の緊密化
https://nextjs.org/blog/next-12https://nextjs.org/blog/next-12
ReactとNext.jsは、上記の記事にもあるようにどんどん連携を深めており、Next.jsは他のフレームワークよりも先行して多くの技術を使用しています。ReactとNext.jsは、上記の記事にもあるようにどんどん連携を深めており、Next.jsは他のフレームワークよりも先行して多くの技術を使用しています。
RSCがNext.jsでリリースされたのは2023年でもう2年近く経っているのに、まだRSCを使えるフレームワークがほとんどないことがその証明です。RSCがNext.jsでリリースされたのは2023年でもう2年近く経っているのに、まだRSCを使えるフレームワークがほとんどないことがその証明です。
今後も優先的に新機能を実装できるだろう点は大きなアドバンテージだと感じました。今後も優先的に新機能を実装できるだろう点は大きなアドバンテージだと感じました。
React 19がフルに活用できるReact 19がフルに活用できる
Remix / React Router v7 だと、route modulesの loader / action でデータを扱うため、コンポーネントとデータ部分が分離しがちでした。
Next.js(App Router)ではRSC (React Server Components) や Server Actions 、useActionState等を活用することで、コンポーネントとデータの距離が縮まり、開発生産性が向上しました。また、コロケーションに基づいたディレクトリ構成がRemixより行いやすくなった点も開発生産性の向上に繋がりました。Remix / React Router v7 だと、route modulesの loader / action でデータを扱うため、コンポーネントとデータ部分が分離しがちでした。 Next.js(App Router)ではRSC (React Server Components) や Server Actions 、useActionState等を活用することで、 コンポーネントとデータの距離が縮まり、開発生産性が向上 しました。また、 コロケーションに基づいたディレクトリ構成 がRemixより行いやすくなった点も開発生産性の向上に繋がりました。
豊富なエコシステム豊富なエコシステム
Next.jsの場合、Sentry、Supabase、Stripe など多くのサービスの公式例があり、周辺ライブラリも充実しています。next/font や next/image も素晴らしく、これによってLight HouseのPerformanceが100点になり、Best Practicesも100点になりました。Next.jsの場合、Sentry、Supabase、Stripe など多くのサービスの公式例があり、周辺ライブラリも充実しています。next/font や next/image も素晴らしく、これによってLight HouseのPerformanceが100点になり、Best Practicesも100点になりました。
移行前は以下の点数だったので、エコシステムの力を大いに感じています。(Accessibility頑張ります。)移行前は以下の点数だったので、エコシステムの力を大いに感じています。(Accessibility頑張ります。)
AIフレンドリーAIフレンドリー
ClaudeやGPTに、Remixで書いてと指示を出してもNext.jsのコードを吐いてくることがよくありました。ユーザが多い分、Next.jsの場合最初から使えるコードを出してくれる割合が増えたと感じます。これもAI時代の新たなエコシステムと呼べるかもしれません。ClaudeやGPTに、Remixで書いてと指示を出してもNext.jsのコードを吐いてくることがよくありました。ユーザが多い分、Next.jsの場合最初から使えるコードを出してくれる割合が増えたと感じます。これもAI時代の新たなエコシステムと呼べるかもしれません。
ビルドが遅いビルドが遅い
Remix は HMR が速く、ローカル開発が非常に快適でした。Next.js はファイルによってはビルドに数秒かかり、時にはリロード必須になるなど、開発体験としてはストレスがあります。Remix は HMR が速く、ローカル開発が非常に快適でした。Next.js はファイルによってはビルドに数秒かかり、時にはリロード必須になるなど、 開発体験としてはストレス があります。
メンタルモデルの複雑さメンタルモデルの複雑さ
Remixのメンタルモデルは、loader→component→actionというシンプルでわかりやすい流れでした。Remixのメンタルモデルは、loader→component→actionというシンプルでわかりやすい流れでした。
Next.jsの場合、RSC/Server Actions/revalidatePath/revalidateTag/next/dynamic/Request Memoizationなど様々な概念が登場します。最適化されたことでLighthouseのスコアは上がりましたが、体感ではRemixの時と速度に差はありません。ユーザとしては体感速ければどうでもいいので、勝手に速くなるようにしてほしいという感想です。Next.jsの場合、RSC/Server Actions/revalidatePath/revalidateTag/next/dynamic/Request Memoizationなど様々な概念が登場します。最適化されたことでLighthouseのスコアは上がりましたが、体感ではRemixの時と速度に差はありません。ユーザとしては体感速ければどうでもいいので、勝手に速くなるようにしてほしいという感想です。
黒魔術黒魔術
Remixの吐くhtmlは非常にシンプルでした。書いたものがそのまま出力されているという感じで、把握しやすかったです。Remixの吐くhtmlは非常にシンプルでした。書いたものがそのまま出力されているという感じで、把握しやすかったです。
Next.js はフレームワーク独自のロジックでclassNameやcanvasタグが差し込まれていたり、デバッグが複雑です。また、Edge Runtimeと絡んで、ローカルでは動くのに本番だと動かない問題にも遭遇しがちです。Next.js はフレームワーク独自のロジックでclassNameやcanvasタグが差し込まれていたり、デバッグが複雑です。また、Edge Runtimeと絡んで、ローカルでは動くのに本番だと動かない問題にも遭遇しがちです。
最初にブランチを切ってcreate next し、ライブラリの移行から始めました。具体的には以下のライブラリを移行する必要がありました。最初にブランチを切ってcreate next し、ライブラリの移行から始めました。具体的には以下のライブラリを移行する必要がありました。
remix-auth → auth.jsremix-auth → auth.js
react-i18next → next-intlreact-i18next → next-intl
remix-auth → auth.jsの変更には、DBスキーマの対応とORMとして使っているPrismaのEdge Runtime対応が必要で、移行全体の中でもここが一番大変でした。remix-auth → auth.jsの変更には、DBスキーマの対応とORMとして使っているPrismaのEdge Runtime対応が必要で、移行全体の中でもここが一番大変でした。
DBスキーマはauth.js側に合わせたスキーマを用意し、既存データをマイグレーションし、カスタマイズを減らすことでアップデート時のリスクを抑えました。DBスキーマはauth.js側に合わせたスキーマを用意し、既存データをマイグレーションし、カスタマイズを減らすことでアップデート時のリスクを抑えました。
Edge Runtimeは、Prisma Accelerate はまだ事例が少なく、ローカルと本番の差異が大きそうで懸念があり、Drizzle への移行は自由度が高い一方で工数が大きいので、結果、Neon + Prisma を選択して最小限の工数に留めました。Edge Runtimeは、Prisma Accelerate はまだ事例が少なく、ローカルと本番の差異が大きそうで懸念があり、Drizzle への移行は自由度が高い一方で工数が大きいので、結果、 Neon + Prisma を選択して最小限の工数に留めました。
次に、既存の Remix のルーティングや loader / action 部分を、Next.js App Router での RSC / Server Actions に置き換えました。
ここではAIが非常に役に立ちました。と言ってもuseActionStateやServer ActionはまだAIには書けないので自力で頑張る必要があります。次に、既存の Remix のルーティングや loader / action 部分を、Next.js App Router での RSC / Server Actions に置き換えました。 ここではAIが非常に役に立ちました。と言ってもuseActionStateやServer ActionはまだAIには書けないので自力で頑張る必要があります。
Server Actions に共通ロジックを持たせない工夫Server Actions に共通ロジックを持たせない工夫
すべての Server Actions が外部公開されるため、認証コードをいちいち書く必要があります。できるだけ認証ロジックをまとめた関数や、Middleware で対応できないか検討しましょう。すべての Server Actions が外部公開されるため、認証コードをいちいち書く必要があります。できるだけ認証ロジックをまとめた関数や、Middleware で対応できないか検討しましょう。
RSC コンポーネントのテストRSC コンポーネントのテスト
RSC は 現状テスト手法が確立されていないので、ビジネスロジックや認証ロジックを分け、Unit テスト可能にするのがおすすめです。RSC は 現状テスト手法が確立されていないので、 ビジネスロジックや認証ロジックを分け 、Unit テスト可能にするのがおすすめです。
useActionState の型付けuseActionState の型付け
useActionStateの返り値に型情報を付与する仕組みはまだ確立されていません。自前で定義するのが現状ベターです。個人的には以下のようにしています。(about_hiroppyさんのtweetを参考にしています。)useActionStateの返り値に型情報を付与する仕組みはまだ確立されていません。自前で定義するのが現状ベターです。個人的には以下のようにしています。( about_hiroppyさんのtweet を参考にしています。)
export type ActionResponse<T = void, U = Record<string, unknown>> = {
success: boolean;
message?: string;
data?: T;
zodErrors?: typeToFlattenedError<U>["fieldErrors"];
};
form の型検証form の型検証
Server Action 内でバリデーションするといいです。個人的にはzodで以下のようにしています。Server Action 内でバリデーションするといいです。個人的にはzodで以下のようにしています。
const editPageContentSchema = z.object({
slug: z.string().min(1),
title: z.string().min(1).max(100),
pageContent: z.string().min(1),
});
const parsedFormData = editPageContentSchema.safeParse({
slug: formData.get("slug"),
title: formData.get("title"),
pageContent: formData.get("pageContent"),
});
小〜中規模のアプリ小〜中規模のアプリ
そこまで最新の React 機能を活用しなくても良ければ、Remix / React Router はシンプルで高速な開発体験を得やすい。メンタルモデルも明快。そこまで最新の React 機能を活用しなくても良ければ、 Remix / React Router はシンプルで高速な開発体験を得やすい。メンタルモデルも明快。
toBやSPAなら個人的にはRemix / React Router。toBやSPAなら個人的には Remix / React Router 。
中〜大規模 or エコシステムを活かしたい場合中〜大規模 or エコシステムを活かしたい場合
Next.jsを選べばサービス・ライブラリとの連携がしやすく、将来的な React のアップデートにも先行して対応できる。Next.js を選べばサービス・ライブラリとの連携がしやすく、将来的な React のアップデートにも先行して対応できる。
toCやSSRをしたいならNext.js。toCやSSRをしたいなら Next.js 。
個人的には「リリースして伸びてきたら Next.js に移行」というアプローチも十分アリだと思います。Remix/React Routerならcloudflareを使えばコストもかなり抑えられます。ただし、移行時はやはり工数も掛かるので、将来的な拡張を見据えている場合は最初から Next.js を使う選択肢も強いです。個人的には「リリースして伸びてきたら Next.js に移行」というアプローチも十分アリだと思います。Remix/React Routerならcloudflareを使えばコストもかなり抑えられます。ただし、移行時はやはり工数も掛かるので、 将来的な拡張を見据えている場合 は最初から Next.js を使う選択肢も強いです。
フレームワーク、DB、インフラの移行と大掛かりな変更だったのですが、重要な部分にテストコードを書いていたことや、DB周りのコードをCQRSで分離していたことで、認証周り以外は比較的スムーズでした。改めて、コードの品質やアーキテクチャに投資することが大事だと実感しています。フレームワーク、DB、インフラの移行と大掛かりな変更だったのですが、重要な部分にテストコードを書いていたことや、DB周りのコードをCQRSで分離していたことで、認証周り以外は比較的スムーズでした。改めて、 コードの品質やアーキテクチャに投資することが大事 だと実感しています。
このサイト「Evame」は、書いた記事やコメントをAIが自動翻訳してくれる多言語コミュニティです。僕もこの移行記を日本語で書き、英語圏にシェアできるようになりました。このサイト「Evame」は、書いた記事やコメントをAIが自動翻訳してくれる多言語コミュニティです。僕もこの移行記を日本語で書き、英語圏にシェアできるようになりました。
もし興味があれば、ぜひ好きな言語で記事を書いて、世界中に発信してみてください!もし興味があれば、ぜひ好きな言語で記事を書いて、世界中に発信してみてください!
https://evame.tech/jahttps://evame.tech/ja
以上が、Remix / React Router v7 から Next.js へ移行した感想と比較、そして移行手順です。何か参考になる部分があれば嬉しいです!以上が、Remix / React Router v7 から Next.js へ移行した感想と比較、そして移行手順です。何か参考になる部分があれば嬉しいです!