404 Not Found

404 Not Found


nginx

SSRとCSRのレンダリングモード

CharlieはMegaShopのすべてのページがSSRを使用していることを発見し, サーバーに大きな負荷がかかっていました。数百万の商品ページがリクエストごとにレンダリングされています。ホームページのコンテンツはめったに変わらないのに, リクエストのたびに再レンダリングされます。管理画面はSEO不要なのにサーバーでレンダリングされています。ハイブリッドレンダリングにより, 各ページに最適な戦略を選べます:ホームページはSSG, 商品一覧はISR, 詳細ページはSSR, ユーザーセンターはCSR。

1. 学ぶ内容


2. アーキテクトのある本当の話

(1) 課題:全ページSSRでサーバーが過負荷

MegaShopは毎秒5,000リクエストを処理し, すべてSSRです。ホームページのコンテンツは1日1回しか変わらないのに, リクエストごとにレンダリングされます。商品一覧は1時間ごとに更新され, 100万商品の詳細ページが繰り返しレンダリングされています。サーバーのCPU使用率は90%に達し, Bobはサーバーを10台追加しようとしています。

(2) ハイブリッドレンダリングの解決策

Nuxt 3のrouteRulesにより, 各ルートに独自のレンダリング戦略を設定できます:

TYPESCRIPT
// nuxt.config.ts
routeRules: {
  '/': { prerender: true },        // ホーム:ビルド時に1回
  '/products': { swr: 3600 },      // 商品一覧:1時間キャッシュ
  '/products/**': { swr: 86400 },  // 商品詳細:1日キャッシュ
  '/admin/**': { ssr: false }      // 管理画面:クライアントのみ
}

(3) メリット:サーバー負荷85%削減

ハイブリッドレンダリングの導入後, SSRリクエストは毎秒5,000から750に減少し, サーバー台数は10台から3台に削減され, コストが70%カットされました。


3. routeRulesレンダリングモード

(1) ハイブリッドレンダリング判定ツリー

100%
flowchart TB
    A[ページリクエスト] --> B{SEOが必要?}
    B -->|いいえ| C[CSR - ssr: false]
    B -->|はい| D{コンテンツは変化する?}
    D -->|しない| E[SSG - prerender: true]
    D -->|めったに| F[ISR - swr: 秒数]
    D -->|頻繁に| G[SSR - デフォルトモード]
    D -->|地域による| H[ESR - エッジレンダリング]
    C --> I[管理画面 / ダッシュボード / ユーザーセンター]
    E --> J[About / 法的情報 / ランディング]
    F --> K[商品一覧 / カテゴリページ]
    G --> L[商品詳細 / 検索結果]
    H --> M[グローバルホーム / 地域コンテンツ]

(2) routeRulesオプションクイックリファレンス

オプション 効果 適用シナリオ
ssr boolean SSRスイッチ false = CSR
csr boolean CSRスイッチ 従来オプション
swr number/string ISRキャッシュ期間 (秒) 定期更新ページ
prerender boolean ビルド時に事前レンダリング 静的ページ
hydrate boolean ハイドレーションのみ, レンダリングなし 特殊最適化
redirect string 301/302リダイレクト URL移行
cors boolean クロスオリジン APIルート
headers object カスタムレスポンスヘッダー キャッシュ/セキュリティ

(1) ▶ サンプル:完全なrouteRules設定

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // ホーム:ビルド時に事前レンダリング
    '/': { prerender: true },

    // 静的ページ
    '/about': { prerender: true },
    '/privacy': { prerender: true },

    // 商品一覧:ISR 1時間キャッシュ
    '/products': { swr: 3600 },

    // 商品詳細:ISR 24時間キャッシュ
    '/products/**': { swr: 86400 },

    // カテゴリ:ISR 6時間キャッシュ
    '/categories/**': { swr: 21600 },

    // 管理画面:クライアント側レンダリングのみ
    '/admin/**': { ssr: false },

    // ユーザーページ:クライアント側
    '/profile/**': { ssr: false },

    // API:CORSヘッダー
    '/api/**': { cors: true },

    // リダイレクト
    '/shop/': { redirect: '/products/', redirectCode: 301 },

    // 静的アセットのカスタムヘッダー
    '/_nuxt/**': { headers: { 'cache-control': 'max-age=31536000' } }
  }
})

出力:

TEXT
// 実行成功

4. ISRインクリメンタル静的再生成

(1) ISRのワークフロー

100%
sequenceDiagram
    participant U as ユーザー
    participant C as CDN/Cache
    participant S as Nuxt Server
    participant D as データベース

    U->>C: GET /products/123
    C->>C: キャッシュHIT? (swr期間内)
    C-->>U: キャッシュ済みHTML (即座)
    
    Note over C,S: swr期間経過後
    U->>C: GET /products/123
    C->>C: キャッシュSTALE
    C-->>U: 古いHTML (高速)
    C->>S: バックグラウンド再検証
    S->>D: 最新データを取得
    D-->>S: 更新データ
    S-->>C: 新鮮なHTML
    Note over C: 次回リクエストで新鮮なHTMLを取得

(2) ISR, SSR, SSGの比較

項目 SSR SSG (prerender) ISR (swr)
レンダリングタイミング 各リクエスト時 ビルド時 キャッシュ期限後のバックグラウンド更新
レスポンスタイム 🟡 中 🟢 最速 🟢 高速 (キャッシュHIT時)
データ鮮度 ✅ 最新 ❌ ビルド時に固定 ⚠️ 最大遅延 = swr秒数
サーバー負荷 🔴 高 🟢 最低 🟢 低
ユースケース リアルタイムデータ 変更なし 定期更新

(1) ▶ サンプル:ISR商品一覧ページ

TYPESCRIPT
// nuxt.config.ts - ISR設定
export default defineNuxtConfig({
  routeRules: {
    // 商品一覧:キャッシュから配信, 60秒ごとに再検証
    '/products': { swr: 60 },
    // 商品詳細:1時間キャッシュ
    '/products/**': { swr: 3600 }
  }
})

出力:

TEXT
// 実行成功

(2) ▶ サンプル:プログラムによるISRキャッシュ無効化

TYPESCRIPT
// server/api/invalidate-cache.post.ts
export default defineEventHandler(async (event) => {
  const { path } = await readBody(event)

  // 特定キャッシュルートをパージ
  await useStorage('cache').removeItem(`nitro:routes:${path}`)

  return { message: `Cache invalidated for ${path}`, timestamp: Date.now() }
})

出力:

TEXT
// 実行成功

5. ESRエッジレンダリング

(1) エッジレンダリングの原理

ESR (Edge Side Rendering)はCDNエッジノードでレンダリングを行い, ユーザーは最も近いノードからHTMLを取得します。

項目 中央SSR ESR
サーバーの場所 単一リージョン グローバルエッジノード
レイテンシ 距離に依存 最小 (ローカルアクセス)
ユースケース 単一リージョンユーザー グローバルユーザー
Nitroプリセット node-server cloudflare-pages / vercel-edge

(1) ▶ サンプル:Vercel Edgeデプロイ設定

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'vercel-edge'
  },
  routeRules: {
    // ホームはグローバルユーザー向けにエッジでレンダリング
    '/': { swr: 3600 },
    '/products/**': { swr: 86400 }
  }
})

出力:

TEXT
// 実行成功

6. MegaShopハイブリッドレンダリング戦略

(1) MegaShopページレンダリング戦略の計画

ページ URL 戦略 SWR 理由
ホーム / SSG - 安定コンテンツ, ビルド時レンダリング
About /about SSG - ほぼ変更なし
商品一覧 /products ISR 3600 1時間ごとに更新
カテゴリ /categories/** ISR 21600 毎日更新
商品詳細 /products/** ISR 86400 毎日更新
検索結果 /search SSR - リアルタイムクエリ
カート /cart CSR - 会員のみ
ユーザーセンター /profile/** CSR - SEO不要
管理画面 /admin/** CSR - SEO不要
API /api/** - - データ専用API

(1) ▶ サンプル:MegaShopの完全なrouteRules

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true,

  routeRules: {
    // SSG:ビルド時に事前レンダリング
    '/': { prerender: true },
    '/about': { prerender: true },
    '/privacy': { prerender: true },
    '/contact': { prerender: true },

    // ISR:再検証付きキャッシュ
    '/products': { swr: 3600 },           // 1時間
    '/categories/**': { swr: 21600 },     // 6時間
    '/products/**': { swr: 86400 },       // 24時間

    // SSR:リアルタイムレンダリング
    '/search': { swr: 0 },

    // CSR:クライアント側のみ
    '/cart': { ssr: false },
    '/checkout/**': { ssr: false },
    '/profile/**': { ssr: false },
    '/admin/**': { ssr: false },

    // API
    '/api/**': { cors: true },

    // 静的アセット:長期キャッシュ
    '/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
    '/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
  }
})

出力:

TEXT
// 実行成功

(2) 数百万商品ページのISRキャッシュ期限切れ対策

イベント 期限切れポリシー 実装方法
商品価格更新 対象商品ページを無効化 useStorage('cache').removeItem()
新商品追加 商品一覧を期限切れ /productsキャッシュを一括無効化
カテゴリ変更 カテゴリページを無効化 /categories/xxxを無効化
サイト全体セール 全商品ページをクリア キャッシュ全体をクリア
在庫変動 対象商品を期限切れ Webhookでトリガー

7. 総合例:MegaShopレンダリングモードの検証

VUE
<!-- pages/admin/dashboard.vue - CSRモード -->
<template>
  <div>
    <h1>Admin Dashboard</h1>
    <p>Rendering mode: Client-Side Only</p>
    <p>Current time: {{ currentTime }}</p>
  </div>
</template>

<script setup lang="ts">
// CSR:このページはサーバーレンダリングされない
definePageMeta({ ssr: false, layout: 'sidebar' })

const currentTime = ref(new Date().toISOString())
onMounted(() => {
  setInterval(() => {
    currentTime.value = new Date().toISOString()
  }, 1000)
})
</script>
VUE
<!-- pages/products/[id].vue - ISRモード -->
<template>
  <div v-if="product">
    <h1>{{ product.name }}</h1>
    <p>${{ product.price }} USD</p>
    <p>Cache: revalidated every 24 hours</p>
  </div>
</template>

<script setup lang="ts">
// ISR:routeRules swr: 86400で24時間キャッシュ
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>

❓ よくある質問

Q swrの単位は何ですか?
A 秒です。swr: 60は60秒のキャッシュを示します。期限後, 次のリクエストでバックグラウンド更新がトリガーされますが, 現在のリクエストは古いキャッシュデータを返します。
Q prerenderとSWRは一緒に使えますか?
A はい。{ prerender: true, swr: 3600 }はビルド時に事前レンダリングし, その後ISRキャッシング戦略で更新することを示します。
Q ISRキャッシュはどこに保存されますか?
A Nitroプリセットに依存します。Node.jsプリセットはメモリまたはファイルシステムに保存, VercelプリセットはVercel KVを使用, CloudflareはKVストレージを使用します。
Q CSRページはSSR時に何を返しますか?
A 空のHTMLテンプレート (<div id="__nuxt"></div>とJSバンドルのみを含む)を返します。全コンテンツはクライアント側でレンダリングされます。
Q 現在のページのレンダリングモードを確認するにはどうすればよいですか?
A ソースコードを表示 (Ctrl+U)—コンテンツがあればSSR/SSG/ISR, 空ならCSRです。レスポンスヘッダーのx-nuxt-render-modeもレンダリングモードを示します。
Q 数百万商品ページのISRキャッシュはストレージを埋め尽くしませんか?
A 人気商品は自然にキャッシュに残り, 不人気なものは期限切れ後に自動的に削除されます。100万ページすべてを事前キャッシュする必要はなく, アクセスされたものだけがキャッシュされます。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度:⭐):routeRulesを設定してホームページの事前レンダリングと管理画面のCSRを有効にし, 「ソースを表示」で2つのモードの出力の違いを確認してください。
  2. 応用問題 (難易度:⭐⭐):商品一覧にISR (swr: 60)を設定し, 60秒後にページをリフレッシュしてデータが更新されるか確認してください。
  3. チャレンジ (難易度:⭐⭐⭐):ISRキャッシュ無効化APIを実装してください。商品データが更新されたら/api/invalidate-cacheを呼び出して対応キャッシュを無効化し, 次回リクエストで新しいデータが取得されることを確認してください。

---|

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%