Comments
Loading Translate Section...Loading Comment Form...
Loading Comment Form...
このWebサイト、『記事もコメントも自動で翻訳される多言語コミュニティEvame』のメタフレームワークをRemixからNext.jsに移行し、インフラもRenderからVercelへ移行しました。이 웹사이트, 『기사도 댓글도 자동으로 번역되는 다국어 커뮤니티 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로 계속 이어지는 줄 알았는데, 위 블로그의 저자이기도 한 공동 설립자가 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도 훌륭해서, 이로 인해 Lighthouse의 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님의 트윗을 참고했습니다.)
export type ActionResponse<T = void, U = Record<string, unknown>> = {
success: boolean;
message?: string;
data?: T;
zodErrors?: typeToFlattenedError<U>["fieldErrors"];
};
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。B2B 또는 SPA라면 개인적으로 Remix/React Router를 추천합니다.
中〜大規模 or エコシステムを活かしたい場合중규모~대규모 또는 에코시스템을 활용하고 싶은 경우
Next.jsを選べばサービス・ライブラリとの連携がしやすく、将来的な React のアップデートにも先行して対応できる。Next.js를 선택하면 서비스 및 라이브러리와의 연동이 쉽고, 미래의 React 업데이트에도 선행하여 대응할 수 있습니다.
toCやSSRをしたいならNext.js。B2C 또는 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로 마이그레이션한 소감과 비교, 그리고 마이그레이션 절차입니다. 조금이라도 참고가 되는 부분이 있으면 좋겠습니다!