【Nuxt.js】Nuxtプロジェクトのディレクトリ構成のベストプラクティス(Next.jsとの対比)

JavaScript

こんにちは、かつコーチです。

Nuxtシリーズもいよいよ最終回です。

これまでpages・layouts・composables・server/apiなど、各ディレクトリの役割を個別に解説してきました。

今回はそれらを俯瞰し、大規模プロジェクトでどう整理していくべきかをまとめます。

あわせて、Next.jsのapp/ディレクトリ構成と対比しながら、両者の設計思想の違いも整理していきます。

Nuxtの主要ディレクトリの役割整理

ルーティング・画面まわり

Nuxtのディレクトリ構成は、役割ごとに明確に分離されているのが特徴です。

ディレクトリ役割
pages/ファイルベースルーティング。1ファイル=1ルート
layouts/ページ共通の外枠(ヘッダー・フッター等)
components/再利用可能なUIパーツ
middleware/ルート遷移前後のガード処理

pages/はルーティング専任、components/は表示専任というように、ファイルの置き場所を見ただけで役割が推測できるようになっています。

ロジック・データまわり

composables/  # 状態・ロジックの共有(useXxxという命名規則)
server/
  api/        # サーバーAPIエンドポイント
  middleware/ # サーバー側ミドルウェア
  utils/      # サーバー専用のユーティリティ
utils/        # クライアント・サーバー両方から使える純粋関数
plugins/      # Vueプラグインの登録

composables/はVueの「合成関数(Composition Function)」をプロジェクト全体で共有するための置き場です。

server/配下は完全にサーバーサイドで完結する処理専用で、クライアントのバンドルには含まれません。

この「クライアントに漏れてはいけないコードをserver/に隔離する」という設計は、APIキーやDB接続情報を扱ううえで重要な安全装置になっています。

Next.jsのapp/ディレクトリとの対比

構成の対応表

Next.jsのApp Routerは、Nuxtとは異なる思想でディレクトリを設計しています。

役割NuxtNext.js(App Router)
ルーティングpages/index.vueapp/page.tsx
共通レイアウトlayouts/default.vueapp/layout.tsx
状態・ロジック共有composables/useXxx.tshooks/useXxx.ts(慣習)
サーバー処理server/api/xxx.tsapp/api/xxx/route.ts またはServer Component内
UIパーツcomponents/components/
ルートガードmiddleware/xxx.tsmiddleware.ts(ルート直下1ファイル)

設計思想の違い

一番大きな違いは、「サーバー処理をどこに書くか」の考え方です。

Nuxtはserver/というディレクトリを明確に切り出し、クライアント処理と物理的に分離します。

一方Next.jsのApp Routerは、Server Componentという概念によって、同じapp/配下でもファイル単位・関数単位でサーバー/クライアントの実行環境が切り替わります。

// Next.js: 同じapp/配下でもデフォルトはサーバー実行
// app/users/[id]/page.tsx
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await getUser(params.id); // サーバーで直接DBアクセスも可能
  return <h1>{user.name}</h1>;
}
<!-- Nuxt: サーバー処理は明示的にserver/api/を経由する -->
<!-- pages/users/[id].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: user } = await useFetch(`/api/users/${route.params.id}`);
</script>

Nuxtは「ディレクトリで環境を分離する」、Next.jsは「ファイル・関数の性質で環境を分離する」と整理すると理解しやすいです。

前者はどこにサーバー専用コードがあるか一目で分かる反面、Next.jsのように1ファイル内でシームレスにサーバー処理を書く柔軟さはありません。

どちらが優れているというより、Vue/Nuxtエコシステムの「明示的な分離を好む」文化が構成に表れていると捉えるとしっくりきます。

大規模プロジェクトでの整理方針

機能単位でのグルーピング

ページ数・コンポーネント数が増えてくると、components/直下にファイルが並びすぎて見通しが悪くなります。

そうなったら、機能(ドメイン)単位でサブディレクトリを切るのがおすすめです。

components/
  user/
    UserCard.vue
    UserList.vue
    UserAvatar.vue
  order/
    OrderTable.vue
    OrderStatusBadge.vue
composables/
  user/
    useUser.ts
    useUserList.ts
  order/
    useOrder.ts

このとき、ディレクトリ名はserver/api/側のリソース名(userorder)と揃えておくと、フロントとバックエンドの対応関係が追いやすくなります。

server/api/のバージョニングと分割

APIが増えてきたプロジェクトでは、server/api/直下もフラットに置きすぎると管理が煩雑になります。

server/
  api/
    v1/
      users/
        [id].get.ts
        index.get.ts
      orders/
        [id].get.ts
        index.post.ts
    v2/
      users/
        [id].get.ts

バージョンディレクトリを切っておくと、破壊的変更が必要になったときに旧バージョンを残しつつ新バージョンを並行稼働させやすくなります。

composablesの粒度をそろえるルール

composables/が増えると、「1関数=1ファイル」なのか「関連する複数関数を1ファイルにまとめる」のか、チーム内で基準がバラつきがちです。

私の運用では、次のルールに落ち着いています。

  • APIフェッチ系(useUseruseOrder等)は1リソース1ファイル
  • UIロジック系(useModaluseToast等)は汎用性が高いためcomposables/ui/にまとめる
  • 3人以上のチームでは、新規composables追加時に既存ファイルへの追記か新規作成かをPRレビューで確認する

ルールを明文化しておかないと、似た名前のuseUserData.tsuseUserInfo.tsが並立する、といった事態が起きやすいので注意してください。

まとめ

この記事のポイント

  • Nuxtはpages/layouts/composables/server/とディレクトリで役割が明確に分離されている
  • Next.js(App Router)はファイル・関数の性質(Server/Client Component)で環境が切り替わる、Nuxtとは異なる思想
  • 大規模化したらcomponents/composables/は機能単位でサブディレクトリ化する
  • server/api/はバージョンディレクトリを切ると破壊的変更に強くなる
  • composablesの粒度ルールはチームで明文化しておくと迷いが減る

次に読むべき記事

Next.js編の「Next.jsプロジェクトのディレクトリ構成のベストプラクティス」とあわせて読むと、React⇄Vueをまたいだ設計の共通点・相違点がより立体的に理解できます。

タグ: #Nuxt.js #上級者向け #設計

タイトルとURLをコピーしました