2025年1月3日 • ☕️☕️☕️ 14 min read

Vue2系のNuxtからReactベースのNext.jsへリプレイスを検討する企業や開発チームが増えてきました。
しかし、フロントエンドのリプレイスは単にフレームワークを切り替えるだけでは終わりません。
技術的負債や既存の仕様・ドキュメント不足など、さまざまな問題が顕在化するためです。
本記事では、自社フードECサイトを実際にNuxtからNext.jsへリプレイスした際に直面した問題点と、導入した施策・学んだことを整理してご紹介します。

tl;dr

NuxtからNext.jsへのリプレイスは、単なるフレームワークの置き換えではなく、既存の技術負債やドキュメント不足といった根本的な課題の解消を伴います。テストやドキュメントの整備を含め、多角的な施策が必要です。

なぜNuxtからNext.jsへ移行したのか

もともとVue2系で構築していたECサイトをVue3系にアップグレードしようと考えていましたが、その移行コスト・互換性などを総合的に検討した結果、**「いっそReactベースに移行する方が長期的にメリットが大きい」**と判断し、Next.jsを選択しました。
最初は個人プロジェクトで「Next.js → Nuxt」へのリプレイスを試したときにそこまで難しくなかったため、逆方向も軽くできるだろうと考えていました。しかし、実際の業務システムを移行すると、想像以上に多くの問題が表面化してきたのです。

実際に直面した問題点

フレームワークを切り替えるだけでなく、既存プロジェクトが抱えている技術的負債やドキュメント不備が大きな壁になりました。以下では具体的にどのような問題があったのかと、その問題点を放置するとどんなリスクがあるのかをまとめました。
(箇条書きでは敬語を使いません)

  1. コンポーネント化されていない

    • 詳細: 1ページが数千行におよぶ単一コンポーネント。業務ロジック・CSS・HTMLがすべて1つのファイルに混在している
    • なぜ問題か:
      • コードの可読性が極端に低い
      • UIとロジックが分離されていないため、再利用や単体テストが難しい
      • 変更の影響範囲が大きく、些細な修正でも別の不具合を引き起こしやすい
  2. JavaScriptによる型定義の欠如

    • 詳細: フロントエンド全体がJavaScriptで実装されており、型注釈が一切存在しない
    • なぜ問題か:
      • データ構造やAPIレスポンスが曖昧で、開発者同士の認識ズレが起こりやすい
      • 型チェックがビルド時に行われず、実行時にエラーを踏むことが多い
  3. バックエンドにDTOやインタフェースが定義されていない

    • 詳細: PHPで作られたバックエンド側に、APIインタフェース(DTO)やリクエスト・レスポンスの型定義が存在しない
    • なぜ問題か:
      • 正確な仕様がわからないため、フロントエンドが誤ったデータを送っても気付きにくい
      • 仕様変更や追加があっても、フロントエンド側と合意が取りづらい
  4. テストコードやCIが存在しない

    • 詳細: 単体テストや統合テストが書かれておらず、継続的インテグレーション(CI)環境も整備されていない
    • なぜ問題か:
      • バグやデグレを自動検知する仕組みがなく、リリース後に大きな問題が発覚しやすい
      • 大幅なリファクタやリプレイスを行う際に影響範囲を把握しづらい
  5. デザイントークンが存在しない

    • 詳細: フォントサイズ・スペーシング・カラーなどの共通化ルールがなく、ページごとにコピペで修正されている
    • なぜ問題か:
      • 同一デザインに見えるコンポーネントでも少しずつ違う実装がある
      • 手戻りや微調整が多く発生し、デザイン崩れの原因になる
  6. レイアウト調整がmarginやpaddingの組み合わせ中心

    • 詳細: flexやgridをほとんど使用せず、ひたすら余白を詰めたり広げたりしてレイアウトを整えている
    • なぜ問題か:
      • レイアウト変更やレスポンシブ対応が大掛かりになり、全体を把握するのが難しい
      • UI崩れが発生した際に原因特定に時間がかかる
  7. 機能一覧が不明

    • 詳細:「このページで何ができるか」という仕様がドキュメント化されておらず、ソースコードと現物合わせで確認するしかない
    • なぜ問題か:
      • リプレイス時に必要な機能を移行し忘れてデグレを起こす可能性が高い
      • 実装者本人も記憶があいまいだと、誰も正確に把握していない機能が生まれる
  8. toCサービス向けの計測実装が不透明

    • 詳細: GTMなどを経由して想定外のパラメータを送っていたり、計測仕様が誰も理解していない
    • なぜ問題か:
      • マーケティング施策の効果測定が正しくできない
      • リプレイス時に計測コードを移行しそこねると売上データやアクセス解析に影響が出る
  9. checkout画面が極度に複雑

    • 詳細: UX向上のためのレアケース対応が積み重なり、stateが膨大かつ複雑になっている
    • なぜ問題か:
      • 変更するたびに新たなバグが生まれやすい
      • 仕様を読み解くのに時間がかかり、移行作業の大きなボトルネックとなる
  10. Nuxtによる新機能開発とNext.jsリプレイスが同時進行

    • 詳細: 既存のビジネスを止められないため、Nuxt版での新機能追加を並行して行いながらNext.jsへ移行する
    • なぜ問題か:
      • 差分をNext.js側に取り込み続ける必要があり、作業が煩雑になる
      • 実装ミスやデグレを発見しづらく、開発メンバーの負荷が大きい

すでに導入されていた施策

私が参加した時点で、すでに以下の施策が取り込まれていました。いずれもリファクタリングやリプレイスにおける土台作りを目的としています。

  • TypeScript化

    • 目的: 型を明示することでバグの早期発見とコードの可読性向上を図る
    • 背景: JavaScriptのままではAPIレスポンスやデータ構造が不透明で、開発者間の認識ずれが頻発していた
  • eslintの導入

    • 目的: コード規約を自動チェックし、一定の品質とスタイルを保つ
    • 背景: 似た機能でも開発者によって書き方がバラバラになり、保守が困難になっていた
  • OpenAPI(Swagger)の導入

    • 目的: API仕様を可視化し、フロントエンドとバックエンドの連携を明確化する
    • 背景: バックエンドにAPIインタフェースの定義がなかったため、リクエスト・レスポンスの正確な型が不明瞭だった
    • 課題: レスポンスの型が確定していない部分も多く、結局ほとんどオプショナル扱いになってしまった
  • コンポーネント化(汎用コンポーネントの作成)

    • 目的: UI要素を再利用しやすくし、ソースコードの重複を減らす
    • 背景: 1つのファイルにロジックやCSSが詰め込まれていたため、保守性・拡張性が大きく損なわれていた
    • 課題:一部分しかできていなく、汎用化しにくいコンポーネントもあった。さらに、各コンポーネントの正確性の確認方法はなかった

追加で打った施策とその背景

私が参加後、さらに以下の施策を進めました。リプレイスを成功させるためには、テストや設計の標準化を徹底することが不可欠だと判断したためです。

  1. ロジックの単体テスト導入(vitest)

    • 目的: バックエンド仕様があいまいな分、フロント側に複雑なバリデーションや分岐ロジックが入りやすい。これらをテストでカバーし、デグレを早期発見する
    • 背景: テストコードが存在せず、コード修正のたびに手動確認のみ。抜け漏れが発生しやすい
  2. Storybookの導入

    • 目的: コンポーネントを単体で切り離し、デザイン差分や動作の不一致を可視化する
    • 背景: コピペで作られた微妙に違うコンポーネントが量産されており、UIの統一感を維持できていなかった
  3. VRT(Visual Regression Testing)の導入(reg-suit)

    • 目的: 視覚的な変更を自動で検出し、意図しないデザイン崩れ(デグレ)を早期に把握する
    • 背景: marginやpaddingを調整したことが他のレイアウトに影響するなど、画面崩れが頻発していた
  4. 似ているコンポーネントの差分吸収(コンポーネント化推進)

    • 目的:「色や余白がほんの少し違うだけ」というコンポーネントを汎用化し、メンテナンス性を高める
    • 背景: コピペ改変による重複実装が多数存在し、修正時に複数箇所を手動で直す必要があった
  5. Swaggerからreact-query(TanStack Query)への自動生成導入

    • 目的: API呼び出しのコードを自動生成し、データ取得処理とキャッシュを効率的に管理する
    • 背景: 手書きのAPI呼び出しで型不一致やキャッシュ管理の不備が多く、開発スピードと品質が両立できなかった
  6. react-queryの導入

    • 目的: 親コンポーネントでまとめてAPIリクエストを行い「バケツリレー」のようにpropsを受け渡すのではなく、子コンポーネントが直接データ取得を担うことでロジックを分散する。これにより、親コンポーネントの責務が軽くなり、保守が容易になる
    • 背景: 以前は「親コンポーネントでデータをfetch→propsで子コンポーネントに受け渡し」という構造が多く、ローディング、エラーなどのstateの確認するため、コードが肥大化していた。react-queryによるデータキャッシュ・fetchの仕組みを導入し、子コンポーネントが必要なデータを直接取得できるようにした
  7. react-hook-formの導入

    • 目的: フォームのバリデーションやステート管理を一元化し、実装をシンプルにする
    • 背景: ページごとに独自のフォーム実装が行われ、バリデーションも分散し混乱していた
  8. レイアウト用コンポーネント(Row、Column)の作成

    • 目的: MUIなどのUIライブラリを参考に、レイアウトの共通パターンを定義し、ページごとの余白調整を最小限に抑える
    • 背景: marginやpaddingを足し引きしてレイアウトしていたため、開発者ごとに書き方がバラバラで崩れが多発した
  9. 複雑すぎるページ(checkoutなど)の作り直し

    • 目的: UX向上のために積み重ねられたレアケース対応を整理し、分岐やstate管理をリセットしてわかりやすくする
    • 背景: 既存のコードは可読性がゼロに近く、チーム全員が「怖くて触れない」状態だった

断念して解決していない問題

これまでに挙げた問題や施策を通じて多くの部分は改善しましたが、以下の項目については現時点で解決を断念、あるいは先送りしており、十分に対処できていません。

  • デザイントークンの完全統一ができていない

    • 現在、新たに作るコンポーネントやページは共通のデザイントークンを用いていますが、旧コンポーネントの一部が未移行です。既存CSSとの整合性やリリースのタイミングを考慮し、段階的な移行にとどまっています。
  • レガシーなPHPバックエンドの仕様刷新

    • バックエンドにはDTOやインタフェースが明示されておらず、フロントのOpenAPI導入もオプショナルばかりという課題が残っています。フロント側で型を厳密に管理しきれない場面が多く、バックエンド側の大幅な書き換えは未着手です。
  • 過去の Nuxt 版との差分ドキュメント化

    • Nuxt版と機能差分が生じており、マーケサイドが気付くまで存在がわからなかった機能もあります。本格的に「機能横断的なドキュメント化を行い、差分を常に追いかける仕組み」を作りたいのですが、リソース不足で断念している状態です。

これらは「将来的には解決したい」と考えているものの、ビジネスを止めずに段階的にリプレイスを進める都合上、優先度を下げて先送りしています。
技術的にはぜひ取り組むべき課題ですが、現実的には“すぐやらないと事業が止まる”ほどではないため、他の優先タスクとの兼ね合いで進められていないのが現状です。

失敗から学んだこと

  • 機能一覧の洗い出し不足
    Nuxt版には実は備わっていた機能が、Next.js版では抜け落ちていてリリース後に発覚。2か月後にマーケチームが気づいたケースもあった
  • リファクタではなくリプレイスに至るプロジェクトには、多様な問題が隠れている
    技術負債が深刻なほど、移行作業は膨大になりがち
  • 仕様書や機能一覧、最低限のドキュメントは必須
    何がどの程度実装されていれば運営できるのか、関係者全員が合意を持てる資料がないまま進むと、バグやデグレの温床になる
  • 品質と開発スピードのトレードオフは事前合意しておく
    自動テストを入れても、スピードと品質が完全には両立しないことが多い。どのラインを満たせばリリースするか、ステークホルダーと明確に決めたほうがいい
  • NuxtかNextかはビジネスにとってそこまで重要ではない
    フレームワーク選定はあくまで手段であり、最終的にはECサイトとして安定的に売上を出せるかどうかが重要
  • リプレイスは想像以上に難易度が高い
    新技術導入だけでなく、既存の技術負債処理・要件洗い出し・ビジネスサイドとの調整といった多層的な課題と向き合わなければならない

まとめ

フロントエンドのリプレイスを進めるうえでは、以下のように多角的な視点が重要だと改めて感じました。

  • 機能一覧や仕様ドキュメントの整備
  • 型定義やテストコードの充実による品質保証
  • デザイントークンやレイアウトパターンの整備によるUIの統一
  • フレームワーク選択を超えた、ビジネス要件や開発速度とのバランスの取り方

「NuxtからNextへ移行」などフレームワークを切り替えるだけでなく、既存コードに潜む負債を洗い出し、仕様を明確化し、テストと設計を標準化することが重要です。
特に、仕様書がない状態でリプレイスを強行すると、リリース後のデグレ発生や機能抜けなどのリスクが高まります
もし、これからリプレイスに取り組まれるのであれば、まずは最低限の機能一覧の把握と品質・スピードの合意形成から始めてみてください。フロントエンドリプレイスの難しさを十分に理解したうえで取り組めば、必ず大きな成果を得られるはずです。


関連投稿

Next.jsでHTTPSを簡単に設定!独自ドメイン対応ガイド

2024年9月3日

ThunderMiracle

Blog part of ThunderMiracle.com