🔥

【Next.js完全ガイド Vol.1】Next.js 15の新機能とアーキテクチャの基礎を徹底解説

に公開

【Next.js完全ガイド Vol.1】Next.js 15の新機能とアーキテクチャの基礎を徹底解説

はじめに

この記事は「Next.js完全ガイド」シリーズの第1回です。全5回でNext.js 15の最新機能から根本的な原理原則まで徹底解説します。

📚 シリーズ構成

この記事で学べること

  • Next.js 15の最新機能と重要な変更点
  • Server ComponentsとClient Componentsの根本的な仕組み
  • RSC Payloadとは何か
  • ミドルウェアの役割と実装方法
  • Next.jsのアーキテクチャ全体像

1. Next.js 15の最新情報

リリース概要

Next.js 15は2024年10月21日に正式リリースされました。この最新バージョンでは、React 19のサポート、キャッシング改善、Turbopackの安定版、新しいAPIなどが導入されています。

さらに、2024年12月にはNext.js 15.1がリリースされ、React 19の安定版サポート、エラーデバッグの改善、新しい認証APIなどが追加されました。

主な新機能

1. React 19サポート

// React 19の新機能が使える
import { use } from 'react';

function Comments({ commentsPromise }) {
  // Promiseを直接扱える
  const comments = use(commentsPromise);
  
  return (
    <ul>
      {comments.map(comment => (
        <li key={comment.id}>{comment.text}</li>
      ))}
    </ul>
  );
}
  • React 19の完全サポートと、React Compiler(実験的機能)の統合
  • より効率的な自動最適化により、手動でのメモ化が不要に

2. キャッシング変更(Breaking Change)

これは重要な変更です!

// Next.js 14: 自動的にキャッシュされる
const data = await fetch('https://api.example.com/data');

// Next.js 15: キャッシュされない(毎回新しいデータを取得)
const data = await fetch('https://api.example.com/data');

// Next.js 15: 明示的にキャッシュする必要がある
const data = await fetch('https://api.example.com/data', {
  cache: 'force-cache'
});
  • fetchリクエスト、GETルートハンドラー、クライアントナビゲーションがデフォルトでキャッシュされなくなりました
  • より予測可能な動作と、明示的なキャッシュコントロールを実現

3. Turbopack Dev(安定版)

# Turbopackを有効化
next dev --turbo
  • 開発時にTurbopackを使用することで、ローカルサーバーの起動が75%高速化
  • Fast Refreshでのコード更新が95%速くなりました

4. Async Request APIs

// Next.js 14
export default function Page({ params, searchParams }) {
  const { id } = params; // 同期
}

// Next.js 15
export default async function Page({ params, searchParams }) {
  const { id } = await params; // 非同期
}
  • より簡素化されたレンダリングとキャッシングモデルへの段階的な移行
  • cookies、headers、paramsなどのAPIが非同期に

5. after API(安定版)

import { after } from 'next/server';

export default async function Layout({ children }) {
  // レスポンス送信後に実行される
  after(async () => {
    await logAnalytics();
    await updateCache();
  });

  return <>{children}</>;
}
  • レスポンスストリーミング完了後にコードを実行できる新しいAPI
  • ロギング、分析、システム同期などに最適

2. Next.jsの根本的な原理原則

サーバーとクライアントの環境分離

Next.jsでは、ネットワーク境界という概念的な線によってサーバーとクライアントのコードが明確に分離されています。

┌─────────────────────────────────────┐
│  サーバー環境                        │
│  - データセンターのコンピューター     │
│  - データベースに近い                │
│  - センシティブな情報を安全に保持     │
└─────────────────────────────────────┘
              ↕️
    【ネットワーク境界】
              ↕️
┌─────────────────────────────────────┐
│  クライアント環境                     │
│  - ユーザーのブラウザ                │
│  - UIをレンダリング                  │
│  - インタラクティブな操作            │
└─────────────────────────────────────┘

この分離により、適切な場所で適切な処理を行い、セキュリティとパフォーマンスを同時に実現しています。


3. Server ComponentsとClient Componentsのアーキテクチャ

Next.jsはReact Server Componentsを使用してサーバーでのレンダリングを可能にし、レンダリング作業は個別のルートセグメント(レイアウトとページ)によって分割されます。

Server Componentsの利点

1. データフェッチの最適化

// Server Component(デフォルト)
export default async function ProductList() {
  // データベースに直接アクセス可能
  const products = await db.products.findMany();
  
  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}
  • データソースに近い場所での処理
  • クライアントへのリクエスト数を削減

2. セキュリティの向上

// Server Component
export default async function UserProfile() {
  // APIキーはサーバーでのみ使用され、クライアントに露出しない
  const userData = await fetch('https://api.example.com/user', {
    headers: {
      'Authorization': `Bearer ${process.env.SECRET_API_KEY}`
    }
  });
  
  return <Profile data={userData} />;
}

3. バンドルサイズの削減

// Server Component
import { HeavyLibrary } from 'heavy-library'; // 10MB

export default function Analytics() {
  const data = HeavyLibrary.process(rawData);
  return <Chart data={data} />;
}
// ↑このライブラリはクライアントに送信されない!

4. 初期ページロードの高速化

サーバーでHTMLを生成して即座に表示し、インタラクティブになるまでの時間を短縮します。

React Server Component Payload (RSC Payload)

RSC Payloadは、サーバーでレンダリングした結果をクライアントに送るための特殊なデータ形式です。

RSC Payloadに含まれる情報

  • レンダリングされたServer Componentsの結果
  • Client Componentsをレンダリングする場所のプレースホルダーと、そのJavaScriptファイルへの参照
  • Server ComponentからClient Componentに渡されるprops

従来のSSRとの違い

【従来のSSR】
サーバー: HTML生成
↓
クライアント: 全てのReactコンポーネントを再実行(重い)

【RSC Payload方式】
サーバー: HTML + RSC Payload生成
↓
クライアント: Client Componentsだけを実行(軽い)

IKEAの例え

RSC PayloadはIKEAの組み立て説明書のようなものです。

【従来の方式】
完成した家具を配送 = 全てのJavaScriptを送信
↓
重い、遅い

【RSC Payload方式】
組み立て説明書 + 部品だけを配送 = 必要最小限のJavaScriptを送信
↓
ブラウザが効率的に組み立て
↓
軽い、速い!

メリット

  • JavaScriptバンドルが小さくなる
  • 初期表示が速い
  • データフェッチが効率的
  • セキュリティが向上

4. ミドルウェア (Middleware)

ミドルウェアとは

ミドルウェアは、リクエストが処理される前に実行される「門番」のような存在です。

レストランの例え

お客さん(ユーザー)
    ↓
🚪 入口(ミドルウェア)
    ├─ 予約確認
    ├─ ドレスコードチェック
    ├─ VIPなら特別室へ案内
    └─ 満席なら別店舗へ案内
    ↓
🍽️ テーブル(ページ)

リクエストの流れ

1. ユーザーが /dashboard にアクセス
    ↓
2. ミドルウェアが実行される
    ↓
3. チェック・処理
    ↓
4. 結果に応じて:
   - そのまま通す
   - リダイレクト
   - 書き換え
   - ブロック
    ↓
5. ページコンポーネントが実行される

基本的な実装

// middleware.js (プロジェクトルートに配置)

import { NextResponse } from 'next/server';

export function middleware(request) {
  console.log('アクセスされたURL:', request.url);
  
  // そのまま通す
  return NextResponse.next();
}

// どのパスで実行するか
export const config = {
  matcher: '/dashboard/:path*'
};

主な使用例

1. 認証チェック

import { NextResponse } from 'next/server';

export function middleware(request) {
  const token = request.cookies.get('auth-token');
  
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*']
};

2. 国際化(i18n)対応

export function middleware(request) {
  const locale = request.headers.get('accept-language')?.split(',')[0] || 'ja';
  const url = request.nextUrl.clone();
  
  if (!url.pathname.startsWith('/ja') && !url.pathname.startsWith('/en')) {
    url.pathname = `/${locale}${url.pathname}`;
    return NextResponse.redirect(url);
  }
  
  return NextResponse.next();
}

動作:

日本からアクセス: /products → /ja/products
米国からアクセス: /products → /en/products

3. A/Bテスト

export function middleware(request) {
  const bucket = Math.random() < 0.5 ? 'a' : 'b';
  const response = NextResponse.next();
  response.cookies.set('ab-test-bucket', bucket);
  
  if (bucket === 'b') {
    const url = request.nextUrl.clone();
    url.pathname = `/experiments/b${url.pathname}`;
    return NextResponse.rewrite(url);
  }
  
  return response;
}

4. セキュリティヘッダーの追加

export function middleware(request) {
  const response = NextResponse.next();
  
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Referrer-Policy', 'origin-when-cross-origin');
  
  return response;
}

実行環境

ミドルウェアは Edge Runtime で実行されます:

┌─────────────────────────────────────┐
│  Edge Runtime (超高速)              │
│  - 世界中のエッジロケーションで実行 │
│  - 低レイテンシー                    │
│  - 限られたAPI                       │
└─────────────────────────────────────┘
        ↓ (必要なら)
┌─────────────────────────────────────┐
│  Node.js Runtime (通常のサーバー)   │
│  - フル機能                          │
│  - データベースアクセス可能          │
│  - ページコンポーネントはここで実行  │
└─────────────────────────────────────┘

完全な認証フローの例

// middleware.js

import { NextResponse } from 'next/server';
import { verifyToken } from '@/lib/auth';

export async function middleware(request) {
  const { pathname } = request.nextUrl;
  
  // 公開ページはスキップ
  const publicPaths = ['/', '/login', '/register'];
  if (publicPaths.includes(pathname)) {
    return NextResponse.next();
  }
  
  // トークン取得
  const token = request.cookies.get('auth-token')?.value;
  
  if (!token) {
    const loginUrl = new URL('/login', request.url);
    loginUrl.searchParams.set('callbackUrl', pathname);
    return NextResponse.redirect(loginUrl);
  }
  
  // トークン検証
  try {
    const user = await verifyToken(token);
    
    // 管理者専用ページ
    if (pathname.startsWith('/admin') && user.role !== 'admin') {
      return new NextResponse('Forbidden', { status: 403 });
    }
    
    // ユーザー情報をヘッダーに追加
    const response = NextResponse.next();
    response.headers.set('X-User-Id', user.id);
    response.headers.set('X-User-Role', user.role);
    
    return response;
    
  } catch (error) {
    const loginUrl = new URL('/login', request.url);
    loginUrl.searchParams.set('error', 'invalid_token');
    return NextResponse.redirect(loginUrl);
  }
}

export const config = {
  matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
};

ページコンポーネントでの使用

// app/dashboard/page.js

import { headers } from 'next/headers';

export default async function Dashboard() {
  // ミドルウェアが設定したヘッダーを取得
  const headersList = headers();
  const userId = headersList.get('X-User-Id');
  const userRole = headersList.get('X-User-Role');
  
  const userData = await fetchUserData(userId);
  
  return (
    <div>
      <h1>ダッシュボード</h1>
      <p>役割: {userRole}</p>
      <p>データ: {userData.name}</p>
    </div>
  );
}

5. Next.jsアーキテクチャの全体像

リクエストからレスポンスまでの完全なフロー

1. ユーザーが /products/123 にアクセス
    ↓
2. 【ミドルウェア実行】(Edge Runtime)
   - 認証チェック
   - 地域判定
   - A/Bテスト振り分け
   - ヘッダー追加
    ↓
3. 【ルーティング】
   - /products/[id] にマッチ
    ↓
4. 【Server Components実行】(Node.js Runtime)
   - 静的部分: すぐに送信
   - 動的部分: データ取得後に送信(ストリーミング)
   - RSC Payload生成
    ↓
5. 【HTMLレスポンス】
   - 初期HTML送信
   - Client Components の JavaScript送信
   - 動的データを順次ストリーミング
    ↓
6. 【ブラウザ処理】
   - HTMLパース・表示
   - JavaScriptダウンロード
   - Client Components ハイドレーション
   - ストリーミングデータの受信・表示
    ↓
7. 完了!ユーザーが操作可能に

各レイヤーの役割

┌─────────────────────────────────────┐
│  ミドルウェア層                      │
│  - 認証・認可                        │
│  - リダイレクト・リライト            │
│  - 国際化                            │
│  - A/Bテスト                         │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│  Server Components層                 │
│  - データフェッチ                    │
│  - ビジネスロジック                  │
│  - RSC Payload生成                   │
│  - HTML生成                          │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│  Client Components層                 │
│  - インタラクション                  │
│  - 状態管理                          │
│  - ブラウザAPI                       │
│  - ユーザー入力処理                  │
└─────────────────────────────────────┘

まとめ

この記事では、Next.js 15の最新機能とアーキテクチャの基礎を学びました:

  • ✅ Next.js 15の主要な新機能(React 19、キャッシング変更、Turbopack)
  • ✅ Server ComponentsとClient Componentsの仕組み
  • ✅ RSC Payloadの役割と従来のSSRとの違い
  • ✅ ミドルウェアによるリクエスト制御
  • ✅ Next.jsの全体的なアーキテクチャ

次回は「レンダリング戦略の完全理解」について解説します。静的レンダリング、動的レンダリング、ストリーミングの3つの戦略を詳しく見ていきます。

お楽しみに!


シリーズ記事

Discussion