デモ
ツールデモページ
SSRとは?
この Demo ページは一般ユーザー向けの実用ツールではなく、サーバーサイドレンダリングの経路を確認するための小さな参照ページです。Next.js App Router のページがサーバー側でデータを取得し、その結果を最初の HTML に含め、同時に現在の言語の翻訳を読み込めることを示します。環境変数、サーバー側の署名付き fetch、API レスポンス形式、locale ルーティング、翻訳ロードなど、共通基盤が正しくつながっているかを確認する用途に向いています。このページが動けば、複雑なツールページを調べる前に SSR の基本経路が健全だと判断できます。失敗する場合は、個別ページより共通設定や API 接続を疑うべきです。
使い方
使い方
- ページはサーバーでバックエンドAPIを自動的に呼び出します
- HTMLにはプリレンダリング済みのコンテンツが含まれるため、クライアント側からの追加リクエストは不要です
- APIから返されたデータを確認してください
- SEO対策が必要なページに最適です
開発時の注意
- このページを、SSR、ロケール読み込み、署名付きサーバーリクエスト、API疎通のスモークテストとしてご利用ください。
- このページの表示に問題がある場合は、特定のツールページをデバッグする前に、共有サーバーの設定を確認してください。
利用シーン
仕組み
この Demo ページは Next.js 16 App Router をベースに構築されており、デフォルトでサーバーサイドレンダリング(SSR)を使用します。ユーザーがページをリクエストすると、Node.js サーバーファーストが React コンポーネントを実行し、バックエンド API を呼び出してデータを取得した後、完全にレンダリングされた HTML を単一のレスポンスでブラウザに返します。ブラウザは HTML の解析と描画を即座に開始するため、ユーザーは JavaScript のダウンロードと実行を待つことなく最初のフレームで完全なコンテンツを確認できます。 これは従来のクライアントサイドレンダリング(CSR)とは対照的です。CSR ではサーバーがほぼ空の HTML シェルと JS リンクを返すだけであるため、ブラウザは JS バンドルのダウンロード、フレームワーク初期化、API 呼び出しによるデータ取得を行い、その後に初めてページをレンダリングします。ユーザーの視点では「しばらく空白の画面が続く」状態であり、検索エンジンクローラーにとってはさらに深刻です。ほとんどのクローラーは JavaScript を実行せず、空のシェルしか参照できません。 SSR の後、Next.js は「ハイドレーション」と呼ばれる JavaScript チャンクを注入します。既存の DOM と React コンポーネントツリーを関連付け、イベントリスナーをバインドしてページをインタラクティブにします。全体のフローは HTML 解析 -> CSS 読み込み -> JS ダウンロード -> ハイドレーション -> インタラクティブとなります。Core Web Vitals の 3 指標のうち、LCP(Largest Contentful Paint)と CLS(Cumulative Layout Shift)は SSR の方が通常良好ですが、INP(Interaction to Next Paint)はハイドレーションの速度とイベントハンドラの実装品質に依存します。
- Next.js App Router: App Router のページはデフォルトで Server Component であり、サーバー上で実行され、追加の設定なしで自動的に HTML を返す。
- SSR vs CSR: SSR はサーバーリソースを消費するが高速なファーストペイントと SEO 友好性を提供。CSR は SEO 不要な社内ダッシュボードやインタラクティブなシナリオに適する。
- 最初のペイントフロー: HTML 解析 -> CSS 読み込みとレンダリング -> JS ダウンロードと実行 -> React ハイドレーション -> ページインタラクティブ。各ステップが LCP に影響。
- SEO クロール対応: Googlebot や Bingbot などの主要クローラーは HTML テキストを直接読み取る。SSR の完全なコンテンツ出力はクロールカバレッジを保証。
- Web Vitals のしきい値: LCP < 2.5 秒、INP < 200ms、CLS < 0.1 が Google の推奨する「良好」しきい値。SSR は LCP と CLS の達成を容易にする。
- 署名付きリクエスト: Demo ページはサーバー上で serverApiFetch を通じてバックエンド API を呼び出し、X-Timestamp、X-Nonce、X-Sign を自動的に付与して認証を完了する。
使用例
SSR(サーバーサイドレンダリング)
// app/page.tsx(デフォルトの Server Component)
export default async function Page() {
const data = await fetch('/api/info').then(r => r.json());
return <div>{data.title}</div>;
}
// HTML には <div>実際のコンテンツ</div> が直接含まれるCSR(クライアントサイドレンダリング)
'use client';
import { useEffect, useState } from 'react';
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/info').then(r => r.json()).then(setData);
}, []);
return <div>{data?.title || 'Loading...'}</div>;
}
// 初回描画の HTML には <div>Loading...</div> しか含まれない初回描画速度の比較
# SSR
FCP: 0.4s LCP: 0.8s TTI: 1.2s
SEO: クローラーは完全なコンテンツを直接取得できる
# CSR
FCP: 0.6s LCP: 1.8s TTI: 2.4s
SEO: クローラーはコンテンツ取得に JS の実行が必要よくある質問
このデモページは実際に何を示していますか?
サーバーサイドレンダリング(SSR)のリファレンスルートです。Next.js App Router のページがサーバー側でデータを取得し、その結果を最初の HTML レスポンスに埋め込んだうえで、正しいローカライズラベルも読み込めることを示しています。エンドユーザー向けのツールではなく、開発者向けのスモークテストです。
データはサーバー側で取得されるのですか、それともブラウザ側ですか?
サーバー側です。このページは React Server Component 内で serverApiFetch(src/lib/sign.ts の署名付きリクエストヘッダーを伴う)を呼び出すため、ブラウザはすでにデータを含んだ HTML ページを受け取ります。初回ロード時にクライアント側 fetch は発生しません。
ページに古いデータが表示されることがあるのはなぜですか?
Next.js は revalidate 設定によって SSR レスポンスをキャッシュすることがあります。リクエストごとに最新データが必要な場合は、'export const dynamic = "force-dynamic"' を設定するか、fetch に {cache: 'no-store'} を渡してください。デモルートではサンプルをシンプルに保つためデフォルトの挙動を使用しています。
このパターンを自分のページにコピーして使えますか?
はい、それが目的です。このルートは、next-intl の getTranslations とサーバー側のデータ取得を組み合わせ、結果をクライアントコンポーネントに渡してインタラクティブにする方法を示しています。layout.tsx と page.tsx をコピーし、データソースを置き換えてください。
内部 API でもリクエスト署名が必要なのはなぜですか?
X-Timestamp / X-Nonce / X-Sign はリプレイ攻撃をブロックし、公開 Web からのリクエストが信頼できるクライアントから発信されていることを保証します。署名キーはログイン中のユーザーごとに発行され、それ以外はデフォルト値を使います。実装は src/lib/sign.ts および src/lib/fetch.ts をご覧ください。
このページは本番ビルドにも含まれますか?
デプロイされたアプリにリファレンスとして含まれていますが、ホームページやツール一覧からはリンクされておらず、ユーザー向けツールとして紹介もされていません。ルート形式でレンダリングされた開発者向けドキュメントとして扱ってください。
プロジェクトの構成を学びたい場合、どこから読めばよいですか?
まずこのページの layout.tsx と page.tsx を見て、続いて src/i18n.ts(ロケール設定)、src/i18n/request.ts(グローバル翻訳の読み込み)、src/lib/load-page-messages.ts(ページごとの翻訳)、src/lib/fetch.ts(署名付きリクエストのラッパー)の順に読み進めるとよいでしょう。