レンダリング戦略
レンダリング方式の特性を理解してClaudeCodeに適切な指示を与えることで、パフォーマンスと開発効率を両立できます。弊社では管理画面ではSPAを優先し、公開ページではSSRとCloudflareキャッシュの組み合わせを基本とします。
弊社の推奨ルール
- 管理画面やダッシュボードはSPA(Single Page Application)で実装
- 公開ページはSSR + Cloudflareキャッシュでパフォーマンス最適化
- ISRは基本的に採用せず、キャッシュ制御はCloudflareで統一管理
- ハイドレーションエラーは開発段階で徹底的に防ぐ
- 完全静的サイトのみSSGを採用
ClaudeCodeでの利用
「管理画面なのでSPAで実装して、初期ローディングUIも追加」
「このLPはSSRで実装、Cloudflareでキャッシュ制御」
「ドキュメントサイトは完全静的なのでSSGで」
「商品詳細ページはSSR必須、ハイドレーション対策も含めて」
SPA(Single Page Application)を管理画面で使う
内部向けツールや管理画面では、開発効率とユーザー体験を重視してSPAを採用します。ページ遷移が高速で、リッチなインタラクションを実装しやすいのが特徴です。
// 管理画面はSPAで実装
'use client';
export default function AdminDashboard() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 認証後にデータ取得
fetchDashboardData()
.then(setData)
.finally(() => setLoading(false));
}, []);
if (loading) return <LoadingUI />;
return <DashboardLayout data={data} />;
}
SPAを選ぶ基準:認証が必要、SEO不要、インタラクティブ性重視、ページ遷移の高速化が必要な場合です。
SSR(Server-Side Rendering)をSEO重要ページで使う
商品詳細ページやランディングページなど、SEOとパフォーマンスが重要な場合はSSRを採用します。初回HTMLに全コンテンツが含まれるため、検索エンジンのクロールに最適です。
// 商品詳細ページはSSRでSEO対策
export default async function ProductPage({ params }) {
const product = await fetchProduct(params.id);
// Cloudflareでキャッシュ制御
headers({
'Cache-Control': 'public, max-age=60, s-maxage=300',
'CDN-Cache-Control': 'max-age=3600',
'Surrogate-Key': `product-${params.id}`, // パージ用
});
return <ProductDetail product={product} />;
}
SSRを選ぶ基準:SEOが必須、動的コンテンツ、初回表示速度が重要、OGP対応が必要な場合です。
SSG(Static Site Generation)を完全静的サイトで使う
ドキュメントサイトやコーポレートサイトなど、更新頻度が極めて低く完全に静的なコンテンツにはSSGを使用します。全ページを事前生成するため、最高速の配信が可能です。
// ドキュメントは全ページ事前生成
export async function generateStaticParams() {
const docs = await fetchAllDocuments();
return docs.map(doc => ({
category: doc.category,
slug: doc.slug,
}));
}
export default async function DocPage({ params }) {
const content = await fetchDocument(params.category, params.slug);
return <Documentation content={content} />;
}
SSGを選ぶ基準:コンテンツが完全に静的、更新時は再ビルドで問題ない、最高速の配信が必要な場合です。
ISRを使わない理由とSSR + Cloudflareで代替
Next.jsのISR(Incremental Static Regeneration)は技術的には優れていますが、弊社では基本的に採用しません。キャッシュ制御をオリジンサーバーではなくCloudflareで統一管理したいためです。
// 推奨しない:ISRでオリジンサーバーがキャッシュ管理
export default async function BlogPage({ params }) {
const article = await fetchArticle(params.slug);
return <Article content={article} />;
}
export const revalidate = 3600; // オリジンサーバーで管理
// 推奨:SSR + Cloudflareでエッジキャッシュ管理
export default async function BlogPage({ params }) {
const article = await fetchArticle(params.slug);
// Cloudflareで一元管理
headers({
'Cache-Control': 'public, max-age=60',
'Cloudflare-CDN-Cache-Control': 'max-age=3600',
'Surrogate-Key': `blog-${params.slug}`,
});
return <Article content={article} />;
}
ISRを使わないメリット:キャッシュの挙動が予測可能、Cloudflareで統一的な制御、パージが簡単、監視・分析が一元化できます。
ハイドレーションエラーを防ぐ設計
SSRを使う場合、サーバーとクライアントでHTMLが不一致になるハイドレーションエラーに特に注意が必要です。
// 推奨しない:ハイドレーションエラーが発生
function Component() {
// ランダム値や日時はサーバーとクライアントで異なる
const id = Math.random();
const now = new Date().toISOString();
return <div id={id}>{now}</div>;
}
// 推奨:クライアントサイドでのみ動的値を使用
function Component() {
const [mounted, setMounted] = useState(false);
const [dynamicValue, setDynamicValue] = useState('');
useEffect(() => {
setMounted(true);
setDynamicValue(new Date().toISOString());
}, []);
return (
<div>
{mounted ? dynamicValue : 'Loading...'}
</div>
);
}
ハイドレーションエラーの主な原因:日時、ランダム値、ブラウザ専用API、条件分岐の不一致です。
Cloudflareキャッシュ戦略
SSRとCloudflareを組み合わせることで、ISRと同等以上の効果を実現します。オリジンサーバーの負荷を最小限に抑えながら、高速配信が可能です。
// ブログ記事のキャッシュ戦略
export default async function ArticlePage({ params }) {
const article = await fetchArticle(params.slug);
// 段階的なキャッシュ設定
headers({
// ブラウザキャッシュは短め
'Cache-Control': 'public, max-age=60',
// CDNキャッシュは長め
'Cloudflare-CDN-Cache-Control': 'max-age=86400',
// カテゴリー別のパージ管理
'Surrogate-Key': `article article-${params.slug} category-${article.category}`,
});
return <Article content={article} />;
}
// 更新時はCloudflare APIでパージ
async function purgeArticleCache(slug: string) {
await fetch('https://api.cloudflare.com/client/v4/zones/{zone}/purge_cache', {
method: 'POST',
headers: {
'X-Auth-Email': process.env.CF_EMAIL,
'X-Auth-Key': process.env.CF_API_KEY,
},
body: JSON.stringify({
tags: [`article-${slug}`],
}),
});
}
キャッシュ戦略のポイント:静的アセットは長期、動的コンテンツは短期、タグベースで細かくパージ制御できます。
よくある問題
SPAでSEOが必要になった
後からSEOが必要になった場合は、Next.jsのdynamic importやメタタグの動的生成で部分的に対応します。重要なページから段階的にSSRに移行します。
キャッシュが更新されない
Cloudflareのパージ APIを使用して明示的にキャッシュをクリアします。Surrogate-Keyを活用して関連するページを一括でパージすることも可能です。
ハイドレーションエラーが解決できない
suppressHydrationWarningは根本解決にならないため使用しません。useEffectでクライアント専用の処理を分離するか、dynamic importでSSRを無効化します。
SSRのパフォーマンスが悪い
データ取得の並列化、Streaming SSR、部分的なキャッシュ、Edge Functionsの活用で改善します。Cloudflareのキャッシュ設定を見直すことも重要です。
レンダリング戦略の選択に迷う
管理画面はSPA、公開ページはSSR + Cloudflareキャッシュ、完全静的サイトはSSGという基本方針に従います。ISRは避けてCloudflareで統一管理します。