はじめに
Next.jsの勉強を始めて SSR, CSR, SSG, ISR
の違いをしっかりと理解したいと思いまとめることにしました。
SSR(Server Side Rendering)
クライアントからのリクエストがあった際に、サーバーはサーバー側でJavaScriptを実行して表示するHTMLをレンダリング(生成)します。そして、完全にレンダリングされたHTMLをクライアントに送信します。
CSRとの違いは、ブラウザ側の仕事量です。CSRでは、クライアント側が「JavaScriptを実行して表示するHTMLを生成する」という仕事がありましたが、SSRではHTMLを生成する仕事はサーバー側にある為、ブラウザは返ってきたHTMLを表示するのみの仕事になります。
メリット
- 常に最新のデータを表示できます
- サーバ側で必要なページのみを生成し、ブラウザはそれを表示するだけなのでページが表示されるまでの速度が早い
デメリット
- ページ遷移の度にサーバーへの通信が発生する
実装
Next.jsではデフォルトでSSR
// app/page.js
async function getServerSideData() {
// サーバーサイドでのデータ取得
const res = await fetch('https://api.example.com/data', { cache: 'no-store' }); //SSRではキャッシュを無効化することが多い
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getServerSideData();
console.log('--- SSR: Data fetched on the server ---');
return (
<div>
<h1>Server-Side Rendered Page</h1>
<p>{data.message}</p>
</div>
);
}
CSR(Client Side Rendering)
CSRは、初期リクエストではJavaScriptバンドルと最小限のHTMLをサーバーから取得し、その後ブラウザ上でJavaScriptを実行してAPIからデータを取得➡DOMを構築➡ページを表示するやり方です。
メリット
- ページ遷移が早い:初回ロード後は、必要なデータのみを取得して画面を更新するため、SPA(Single Page Application)のような感覚で高速なページ遷移が可能です
デメリット
- 初回表示が遅い:JavaScriptのダウンロードと実行、APIからのデータ取得に時間がかかります
実装
// app/client-page.js
'use client'; // このディレクティブがCSRを有効にする
import { useState, useEffect } from 'react';
export default function ClientSidePage() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
console.log('--- CSR: Fetching data on the client ---');
const fetchData = async () => {
try {
const res = await fetch('https://api.example.com/data');
if (!res.ok) {
throw new Error('Failed to fetch data');
}
const result = await res.json();
setData(result.message);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
fetchData();
}, []); // 空の依存配列で、コンポーネントマウント時に一度だけ実行
if (loading) {
return <p>Loading data...</p>;
}
return (
<div>
<h1>Client-Side Rendered Page</h1>
<p>{data}</p>
</div>
);
}
SSG(Static Site Generation)
SSGは、アプリケーションのビルド時に、すべてのページを事前にHTMLファイルとして生成する手法です。生成されたHTMLファイルは、CDN(Content Delivery Network)などを通じて高速に配信されます。
メリット
- 静的ファイルとして配信されるため、CDNのキャッシュを最大限に活用できます
- ビルド時に生成された静的なHTMLが配信されるため、リクエストごとにサーバーでの処理が不要です
デメリット
- ページ数が多ければビルドに時間がかかります
実装
// app/static-page.js
async function getStaticData() {
// ビルド時にデータが取得され、HTMLに埋め込まれる
// デフォルトのfetchオプションは { cache: 'force-cache' }
const res = await fetch('https://api.example.com/static-data');
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function StaticPage() {
const data = await getStaticData();
console.log('--- SSG: Data fetched at build time ---');
return (
<div>
<h1>Static Site Generated Page</h1>
<p>{data.message}</p>
</div>
);
}
ISR(Incremental Static Regeneration)
ISRは、SSGの高速性を維持しながら、コンテンツの更新を可能にする方法です。ビルド時にページを生成し、その後、指定した感覚(revalidate
秒)でバックグラウンドでページを再生成します。ユーザーからのリクエストがあった際に、最新のデータがあればそちらを、なければ古いデータを一時的に返します。
メリット
- 常にリクエストごとにレンダリングするわけではないため、SSRよりサーバー負荷を抑えられます
- 高速な配信を維持しつつ、定期的にコンテンツを更新できます
デメリット
-
revalidate
の間隔によっては、変更がユーザーに反映されるまでにタイムラグが生じます - ISRであっても、ビルド時にはデータが固定されるため、ビルド後すぐに最新の情報が必要な場合は不向きです
実装
// app/revalidated-page.js
async function getRevalidatedData() {
// 60秒ごとにバックグラウンドで再生成を試みる
const res = await fetch('https://api.example.com/revalidate-data', {
next: { revalidate: 60 } // 60秒ごとに再生成
});
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function RevalidatedPage() {
const data = await getRevalidatedData();
console.log('--- ISR: Data fetched, with revalidation every 60s ---');
return (
<div>
<h1>Incremental Static Regeneration Page</h1>
<p>{data.message}</p>
</div>
);
}
まとめ
手法 | レンダリングタイミング | SEO | 初期表示速度 | サーバー負荷 | データ鮮度 | 主な用途 |
---|---|---|---|---|---|---|
SSR | リクエストごと(サーバー) | ◎ | 〇 | ◎ | ◎ | ログイン情報など動的なコンテンツ、SEO重視 |
CSR | ブラウザ実行時(クライアント) | △ | △ | 〇 | 〇 | インタラクティブなUI、管理画面 |
SSG | ビルド時 | ◎ | ◎ | 〇 | △ | ブログ、ドキュメント、マーケティングサイト |
ISR | ビルド時 & 定期再生成 | ◎ | ◎ | 〇 | 〇 | 更新頻度の低いブログ、ECサイトの商品情報 |