一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • Next.js 14のApp Routerで構築するモダンWebアプリケーション

App Routerがもたらすパラダイムシフト

Next.js 14で安定版となったApp Routerは、Pages Routerから大きく設計思想が変わりました。React Server Components(RSC)を基盤とし、サーバーとクライアントの境界を明確に分離することで、パフォーマンスと開発体験の両方を向上させています。

筆者は実際にPages Routerで構築された中規模プロジェクトをApp Routerに移行した経験があります。その過程で得た知見と、設計上の重要なポイントを本記事で共有します。

ディレクトリ構成とルーティングの基本

App Routerではappディレクトリ配下のファイル構成がそのままルーティングに対応します。Pages Routerとの最大の違いは、レイアウトの入れ子構造がフォルダ構成で自然に表現できる点です。

app/
├── layout.tsx # ルートレイアウト
├── page.tsx # トップページ (/)
├── globals.css
├── dashboard/
│ ├── layout.tsx # ダッシュボード共通レイアウト
│ ├── page.tsx # /dashboard
│ ├── analytics/
│ │ └── page.tsx # /dashboard/analytics
│ └── settings/
│ └── page.tsx # /dashboard/settings
├── blog/
│ ├── page.tsx # /blog(記事一覧)
│ └── [slug]/
│ └── page.tsx # /blog/:slug(記事詳細)
└── api/
└── posts/
└── route.ts # API Route: /api/posts

レイアウトの活用

レイアウトはページ遷移時に再レンダリングされません。これにより、サイドバーやナビゲーションの状態が保持され、ユーザー体験が大幅に向上します。

// app/dashboard/layout.tsx
import { Sidebar } from '@/components/Sidebar'
import { Header } from '@/components/Header'

export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (





{children}



)
}

Server ComponentsとClient Componentsの使い分け

App Routerでは、デフォルトで全てのコンポーネントがServer Componentです。クライアント側のインタラクションが必要な場合のみ、'use client'ディレクティブを付与します。この判断基準が設計上最も重要なポイントです。

Server Componentが適している場面

  • データベースやAPIからのデータ取得
  • 静的なUIの表示(テキスト、画像、リスト表示)
  • 機密情報(APIキー等)を扱う処理
  • サーバーサイドの重い計算処理

Client Componentが必要な場面

  • useStateuseEffectなどのReact Hooksの使用
  • ブラウザAPIへのアクセス(localStorage、window等)
  • イベントハンドラ(onClick、onChange等)
  • サードパーティライブラリでブラウザ機能に依存するもの
// app/blog/[slug]/page.tsx — Server Component
import { getPost } from '@/lib/posts'
import { LikeButton } from '@/components/LikeButton'
import { CommentSection } from '@/components/CommentSection'

export default async function BlogPost({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)

return (

{post.title}


{post.publishedAt}



{/* インタラクティブな部分だけClient Component */}



)
}

データフェッチングのベストプラクティス

App Routerでは、Server Component内で直接async/awaitを使ってデータを取得できます。これはPages RouterのgetServerSidePropsgetStaticPropsに代わる仕組みです。

fetchのキャッシュ戦略

// デフォルト: 静的にキャッシュ(ビルド時に取得、再利用)
const data = await fetch('https://api.example.com/posts')

// キャッシュ無効: リクエストごとに最新データを取得
const data = await fetch('https://api.example.com/posts', {
cache: 'no-store',
})

// 時間ベースの再検証: 60秒ごとにキャッシュを更新
const data = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
})

実務では、ページの特性に応じてキャッシュ戦略を選択します。ブログ記事のような更新頻度の低いコンテンツには時間ベースの再検証を、ダッシュボードのリアルタイムデータにはno-storeを適用するのが一般的です。

Server Actionsによるフォーム処理

Server Actionsは、フォームの送信処理をサーバー側で直接実行できる機能です。API Routeを別途作成する必要がなく、コードの見通しが良くなります。

// app/contact/page.tsx
export default function ContactPage() {
async function submitForm(formData: FormData) {
'use server'
const name = formData.get('name') as string
const email = formData.get('email') as string
const message = formData.get('message') as string

await db.insert(contacts).values({ name, email, message })
redirect('/contact/thanks')
}

return (






)
}

Server Actionsはプログレッシブエンハンスメントにも対応しており、JavaScriptが無効な環境でもフォーム送信が機能します。これは従来のSPAでは実現が難しかった利点です。

移行時の注意点と実務的なアドバイス

Pages RouterからApp Routerへの移行は段階的に行うことを強く推奨します。Next.jsは両方のルーターを同時に使用できるため、新規ページからApp Routerで作成し、既存ページは順次移行するアプローチが安全です。

  • 状態管理ライブラリとの互換性を事前に確認します。Server Componentsではクライアント側の状態管理は使えないです
  • サードパーティライブラリがRSCに対応しているか確認します。未対応の場合はClient Componentでラップします
  • テスト戦略の見直しも必要。Server Componentsのテストは従来と異なるアプローチが求められます
  • デプロイ環境がNode.jsランタイムをサポートしているか確認します。静的エクスポートでは一部機能が制限されます

App Routerは学習コストが高いものの、正しく活用すればパフォーマンスと開発効率の両面で大きな恩恵があります。まずは小規模なプロジェクトで試し、チーム全体の理解を深めてから本格導入することをお勧めします。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア