11
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【2026年版】Next.js (App Router) の実務ディレクトリ構成&設計ベストプラクティス 🛠️

Posted at

こんにちは!
株式会社プロドウガ@YushiYamamotoです。
らくらくサイトの開発・運営をしながら、Next.js専門の技術顧問として複数のプロジェクトの設計レビューを行っています。

Next.js の App Router、最高ですよね。
でも、「ディレクトリ構成、自由すぎて正解がわからない問題」 に直面していませんか?

  • components フォルダが肥大化してカオス」
  • 「Server Actions はどこに書くのが安全?」
  • 「ページ固有のコンポーネントと、汎用パーツが混ざって管理不能」

今回は、これら全ての悩みを解決する、2026年時点での「実務の正解」とも言えるディレクトリ構成を公開します。
スケーラビリティとメンテナンス性を最優先した、プロの設計図です。


1. 設計の基本思想:「Colocation」と「Features」 📐

かつての Pages Router 時代は、components/ hooks/ utils/ のように「ファイルの種類」で分けるのが主流でした(Layered Architecture)。
しかし、App Router 時代は 「機能単位(Features)」 で分け、「使う場所の近くに置く(Colocation)」 のが鉄則です。

✅ なぜ Features なのか?

認証機能に関わる修正をする時に、pages/auth, components/auth, hooks/auth, utils/auth... とあちこち飛び回りたくないですよね?
「認証機能に関わるものは、全て src/features/auth の中にある」 状態を目指します。


2. 【結論】これが最強のディレクトリ構成だ 🌳

まずは、完成形のツリー構造をお見せします。これをそのまま真似してください。

src/
├── app/                  # ルーティング定義のみ(ロジックは書かない)
│   ├── (public)/         # LPなど
│   ├── (app)/            # アプリ本体
│   │   ├── dashboard/
│   │   │   └── page.tsx  # Featureを呼び出すだけ
│   │   └── layout.tsx
│   ├── api/              # Route Handlers
│   ├── globals.css
│   └── layout.tsx
│
├── components/           # アプリ全体で使う「汎用」コンポーネント
│   ├── ui/               # shadcn/ui (Button, Input等)
│   ├── layouts/          # Header, Footer, Sidebar
│   └── elements/         # その他汎用パーツ (Loading, Error等)
│
├── features/             # ★ここが主役!機能単位で切る
│   ├── auth/             # 認証機能
│   │   ├── components/   # LoginForm, SignupButton (Specific)
│   │   ├── actions/      # Server Actions (login, logout)
│   │   ├── hooks/        # useAuth
│   │   └── types/        # 認証系型定義
│   │
│   ├── posts/            # 投稿機能
│   │   ├── components/   # PostList, PostCard
│   │   ├── api/          # データ取得関数 (getPosts)
│   │   └── actions/      # createPost, deletePost
│   │
│   └── users/            # ユーザー機能
│
├── lib/                  # 外部ライブラリの設定・ラッパー
│   ├── prisma.ts         # DB接続
│   ├── utils.ts          # shadcn用ユーティリティ
│   └── stripe.ts
│
└── types/                # アプリ全体で使う型(env.d.ts等)


3. 「features」ディレクトリ戦略の詳細 🏗️

この構成の肝は src/features です。
ページ(app/)は単なる「ルーティングとレイアウトの枠」に徹し、実質的なコンテンツは全て features から import して組み立てます。

メリット

  1. 影響範囲が明確: features/posts をいじっても、認証機能 (auth) が壊れる心配がない。
  2. 削除が容易: 機能が不要になったら、フォルダごと消せばOK。
  3. App Router との相性: Server Components と Client Components が混在しても、機能単位でまとまっていれば管理しやすい。

❌ やってはいけないこと

features の中で、他の features を直接 import しまくること(相互依存)。
汎用的なものは components/lib/ に逃がし、依存関係を一方向に保つのがコツです。


4. UIコンポーネント管理:Atomic Design の現代的解釈 🎨

「Atomic Design はもう古い?」という議論がありますが、概念自体は有効です。
ただし、App Router 時代は以下のように 2層に簡略化 するのが現実解です。

分類 ディレクトリ 役割 依存関係
Generic (汎用) src/components/ui shadcn/uiなどの最小単位。


ビジネスロジックを持たない。 | どこからでも呼べる |
| Specific (固有) | src/features/**/components | 特定の機能に紐づくUI。


データ取得やActionを含むこともある。 | app から呼ばれる |

shadcn/ui の扱い

components/ui には、npx shadcn-ui@latest add で追加されたコンポーネントだけを置きます。
これらは 「自分たちのデザインシステム」 として扱い、勝手にビジネスロジックを混ぜないようにします。


5. Server Actions の置き場所とセキュリティ 🔒

Server Actions は「関数」ですが、実態は API エンドポイントと同じです。
セキュリティと型安全性を担保するために、以下のルールを設けます。

ルール1:Feature 内の actions.ts に配置する

src/actions/ に全部まとめるのではなく、機能ごとに配置します。

// src/features/posts/actions/create-post.ts
'use server';

import { z } from 'zod';
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

// 入力バリデーションスキーマ
const schema = z.object({
  title: z.string().min(1),
  content: z.string(),
});

export async function createPostAction(formData: FormData) {
  // 1. バリデーション
  const validated = schema.parse({
    title: formData.get('title'),
    content: formData.get('content'),
  });

  // 2. 認証チェック (必須!)
  // const session = await auth();
  // if (!session) throw new Error('Unauthorized');

  // 3. DB操作
  await prisma.post.create({ data: validated });

  // 4. キャッシュ更新 & リダイレクト
  revalidatePath('/posts');
  redirect('/posts');
}

ルール2:Zod で必ず検証する

Client から送られてくるデータは信用してはいけません。必ず Server Action の冒頭で Zod 等を使ってバリデーションします。


6. データフェッチの鉄則:RSCファースト 📡

「どこで fetch するか?」の答えはシンプルです。
「できるだけページ(Server Component)のトップレベルで行う」 です。

✅ 推奨パターン

app/posts/page.tsx (Server) でデータを取得し、features/posts/components/PostList.tsx (Server/Client) に Props で渡す。

// src/app/posts/page.tsx
import { getPosts } from '@/features/posts/api/get-posts';
import { PostList } from '@/features/posts/components/post-list';

export default async function PostsPage() {
  // ここでFetch!
  const posts = await getPosts();

  return (
    <main>
      <h1>投稿一覧</h1>
      {/* データを渡すだけ */}
      <PostList posts={posts} />
    </main>
  );
}

こうすることで、「データ取得(Backend)」「表示(UI)」 の境界が明確になり、パフォーマンスチューニング(Streaming や Suspense)もしやすくなります。


7. まとめ

App Router のディレクトリ構成に「絶対の正解」はありませんが、今回紹介した 「Feature-based Architecture(機能単位アーキテクチャ)」 は、多くの中規模〜大規模開発で採用されている、現時点で最も堅牢なパターンの一つです。

  1. app はルーティング定義のみ
  2. features に機能ごとのロジックとUIを凝集させる
  3. components/ui は shadcn などの汎用パーツ置き場

迷ったら、まずはこの構成で始めてみてください。後から「あ、これ分けといてよかった!」と思う瞬間が必ず来ます。


🚀 エンジニアの方へ:10万円もらえる特別キャンペーン

現在、フリーランスエージェントの 「クラウドワークステック」 が、紹介経由での登録&稼働で「10万円(税込)」がもらえる 異例のキャンペーンを実施中です。

「自分の市場価値(単価)を知りたい」「週3日からの副業・リモート案件を探している」という方は、ぜひこの機会に登録してみてください。

👉 **【限定特典付き】10万円キャンペーンに参加する**

※キャンペーンは予告なく終了する場合があります。

最後に:業務委託のご相談を承ります

私は業務委託エンジニアとして、Next.jsを用いた大規模開発のアーキテクチャ設計や、技術顧問としての支援を行っています。

「既存のNext.jsプロジェクトがカオスになってきたので整理したい」「新規開発の技術選定から相談に乗ってほしい」といったご要望がありましたら、お気軽にご相談ください。

👉 ポートフォリオ
📩 お問い合わせ: お気軽にご連絡ください

11
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?