こんにちは!
株式会社プロドウガの@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 して組み立てます。
メリット
-
影響範囲が明確:
features/postsをいじっても、認証機能 (auth) が壊れる心配がない。 - 削除が容易: 機能が不要になったら、フォルダごと消せばOK。
- 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(機能単位アーキテクチャ)」 は、多くの中規模〜大規模開発で採用されている、現時点で最も堅牢なパターンの一つです。
-
appはルーティング定義のみ -
featuresに機能ごとのロジックとUIを凝集させる -
components/uiは shadcn などの汎用パーツ置き場
迷ったら、まずはこの構成で始めてみてください。後から「あ、これ分けといてよかった!」と思う瞬間が必ず来ます。
🚀 エンジニアの方へ:10万円もらえる特別キャンペーン
現在、フリーランスエージェントの 「クラウドワークステック」 が、紹介経由での登録&稼働で「10万円(税込)」がもらえる 異例のキャンペーンを実施中です。
「自分の市場価値(単価)を知りたい」「週3日からの副業・リモート案件を探している」という方は、ぜひこの機会に登録してみてください。
※キャンペーンは予告なく終了する場合があります。
最後に:業務委託のご相談を承ります
私は業務委託エンジニアとして、Next.jsを用いた大規模開発のアーキテクチャ設計や、技術顧問としての支援を行っています。
「既存のNext.jsプロジェクトがカオスになってきたので整理したい」「新規開発の技術選定から相談に乗ってほしい」といったご要望がありましたら、お気軽にご相談ください。
👉 ポートフォリオ
📩 お問い合わせ: お気軽にご連絡ください