目次
SSRとは
SSR(Server Side Rendering)は、Webページの表示に必要なHTMLを、サーバー側でレンダリング(生成)する技術を指します。以下で仕組みやメリット、デメリットについて紹介します。
SSRの仕組み
SSRでは、クライアント(ブラウザ)からのリクエストに応じて、サーバーがデータ取得とJavaScriptを実行し、完成したHTMLを動的にレンダリングして返します。
クライアント側はHTMLを受け取ってすぐに表示できるため、高速な初期表示ができます。
SSRのメリット
SSRは、特に動的なコンテンツを扱うWebサイトにおいて数多くのメリットをもたらします。
SEOに強い
サーバーサイドでリクエストごとにデータが反映されたHTMLが生成されるため、検索エンジンのクローラーはJavaScriptを実行せずにページ内容を評価できます。
そのおかげで素早く正確にインデックスが登録されるため、検索上位に表示される可能性が高まります。
ユーザーに合わせたコンテンツ表示
リクエスト情報(クッキーや認証トークンなど)からユーザーを特定することで、「マイページ」のようにユーザーそれぞれに最適化されたページをレンダリングできます。
これにより、サイトの利用頻度や満足度を向上させる効果が期待できます。
リアルタイム性
リクエストのたびにサーバーでレンダリングとデータ取得が実行されるため、データベースやAPIの最新情報が即座にページへ反映されます。
ビルド時の情報に固定されたり、一定時間古いキャッシュが表示されたりする静的な手法とは異なるアプローチであり、ユーザーは「今、この瞬間」の情報を得ることができます。
SSRのデメリット
多くの利点を持つSSRですが、その特性上、考慮すべきデメリットも存在します。
サーバへの負荷
リクエストのたびにサーバーで処理が実行されるため、アクセス数が増えるとサーバーへの負荷が大きくなります。
CPUやメモリを多く消費すると、サーバーの費用も高くなる傾向があるため注意が必要です。
ページ遷移時の遅延
ページ遷移をするとその都度サーバーにリクエストが送られるため、他のレンダリング手法が用いられているサイトと比較すると、ページ遷移時に読み込みが長く感じる場合があります。
キャッシング戦略の複雑化
動的に生成されるHTMLを効率的に配信するには、CDN やサーバーのキャッシュを適切に設定することが必要です。
特にユーザーごとに表示内容が異なるページや、頻繁に更新されるページでは、管理が困難になる傾向があります。
レンダリング手法の比較
Next.jsでは、SSRの他にレンダリング戦略を複数提供しています。プロジェクトの要件やページの特性に応じて最適な手法を選択するためには、それぞれの違いを正確に理解することが不可欠です。
SSGとの比較
SSG(Static Site Generation)は、ビルド時にすべてのページのHTMLを事前に生成しておく手法です。
SSGが優れている点
ビルド時に全てのHTMLがレンダリングされるため、リクエスト時はサーバーサイドで処理はほとんど発生しません。そのためコストも低く抑えられ、CDNとの親和性も高いため、大量アクセスへの対応も可能です。
SSGの課題点
サイトのページ数が多い大規模サイトの場合、全ページをビルドするために長時間かかる可能性があります。
また、ビルド後にデータが更新されても、再ビルドしない限りページ内容は変わりません。
そのため、SNSのタイムラインのようにリアルタイム性が求められるコンテンツや、ユーザーごとに表示内容が変わるページには対応できません。
ISRとの比較
ISR(Incremental Static Regeneration)は、SSGの利点を維持しつつ、データの鮮度を保つための仕組みです。
ISRが優れている点
基本的にはSSGと同様にキャッシュされた静的なHTMLを返すため、高速な表示が可能です。 一定時間(例えば60秒ごと)経過した後のアクセスをトリガーとして、ページを再ビルドし、キャッシュを更新することができます。
また、サイト全体を一度にビルドするのではなく、必要に応じてページ単位で再ビルドを行うため、ビルド時間への影響を抑えることができます。
ISRの課題点
レンダリングされていないページに対して初めてアクセスされる時は、サーバーサイドでレンダリング処理が行われるため、若干の待ち時間が発生する場合があります。
再ビルドが行われるタイミングによっては、ユーザーが一時的に古いデータを目にする可能性があります。そのため、リアルタイム性が求められるコンテンツには不向きと言えます。
CSRとの比較
CSR(Client-Side Rendering)は、ブラウザ側でJavaScriptを実行してページをレンダリングする手法です。
CSRが優れている点
一度初期ロードが完了すれば、その後は必要なデータのみを取得し差分を更新します。これにより、ページ遷移がスムーズとなり、快適なユーザー体験を提供できます。
また、ページ遷移などユーザーのアクションのたびにデータ取得するため、リアルタイム性の高いコンテンツに適しています。
CSRの課題点
初回アクセス時、ほぼ空のHTMLと大きなJavaScriptファイルをダウンロードするため、コンテンツ表示までに時間がかかる傾向があります。
また、最初に返されるHTMLにコンテンツが含まれていないため、クローラーが内容を把握できず、SEOの観点から不向きです。
SSRが適しているケース
各レンダリング手法との比較を踏まえ、SSRがその真価を発揮できるケースを具体的に解説します。
ユーザーごとに表示内容が動的に変化するページ
閲覧しているユーザーに合わせた情報を提供するページにSSRは最適です。例えば、ログインユーザーのプロフィールページ、過去の購買履歴、パーソナライズされた推薦情報が表示されるダッシュボードなどが挙げられます。
SSGでは事前にHTMLを生成するためユーザー個別対応が難しく、CSRでもクライアント側でデータを取得・レンダリングするまでにタイムラグが生じますが、SSRならユーザーからのリクエストに応じて最適化されたHTMLをサーバーで生成し、即座に提供できます。
常に最新のコンテンツを提供するページ
情報の鮮度が重要になるコンテンツでは、SSRが力を発揮します。リアルタイムな株価情報サイト、スポーツの試合速報、SNSのタイムライン、または速報性が求められるニュースサイトなどが該当します。
SSGやISRでは、ビルド時やキャッシュ更新時以外は古い情報が表示される可能性がありますが、SSRはリクエストごとに最新のデータをサーバーから取得し、生成されたHTMLとして即座に提供します。これにより、「今、この瞬間」の情報を確実にユーザーに届けられます。
Next.jsにおけるSSRの実装方法
現在、Next.jsでは Pages RouterとApp Routerの2種類のルーティング方式が利用可能です。それぞれの実装方法について詳細に解説します。
Pages Routerでの実装
Pages RouterでSSRを実装する場合、getServerSideProps
という非同期関数をエクスポートします。
この関数は、ページへのリクエストがあるたびにサーバーサイドで実行されます。getServerSideProps
の主な役割は、ページコンポーネントを描画するために必要なデータを事前に取得し、propsとしてコンポーネントに渡すことです。
// pages/example.tsx (または .js)
import { GetServerSideProps, NextPage } from 'next';
interface PageProps {
data: {
userId: number;
id: number;
title: string;
completed: boolean;
};
}
// ページコンポーネント
const ExamplePage: NextPage<PageProps> = ({ data }) => {
return (
<div>
<h1>SSR Example Page</h1>
<p>User ID: {data.userId}</p>
<p>Title: {data.title}</p>
<p>Completed: {data.completed ? 'Yes' : 'No'}</p>
</div>
);
};
export default ExamplePage;
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await res.json();
return {
props: {
data,
},
};
};
この例では、getServerSideProps内でAPIからデータをフェッチし、その結果をpropsオブジェクトに含めて返しています。このpropsが、ExamplePageコンポーネントに渡され、サーバーサイドでレンダリングされます。
App Routerでの実装
App Routerでは、ページ単位ではなくコンポーネント単位でレンダリング手法を選択します。SSRと同様にレンダリングしたい場合はサーバーコンポーネントを使用します。
サーバーコンポーネント内では、async/await
を用いて直接データ取得処理を記述できます。
// app/example/page.tsx
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
// データフェッチをコンポーネント内で行う
async function getData(): Promise<Todo> {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1', {
cache: 'no-store', // 常に最新データを取得
});
return res.json();
}
// ページコンポーネント自体をasync関数として定義
export default async function ExamplePage() {
const data = await getData();
return (
<div>
<h1>SSR Example Page (App Router)</h1>
<p>User ID: {data.userId}</p>
<p>Title: {data.title}</p>
<p>Completed: {data.completed ? 'Yes' : 'No'}</p>
</div>
);
}
この例では、ページコンポーネント自体が非同期関数となっており、内部でawait fetch(...)を実行してデータを取得しています。{ cache: 'no-store' }オプションは、Next.jsの拡張fetch APIに対して、キャッシュを利用せずリクエストごとにデータを再取得するように指示するもので、SSRを実現するための重要な設定です。
まとめ
本記事では、Next.jsにおけるSSRに焦点を当て、その基本的な仕組みから、SSGやISR、CSRといった他のレンダリング手法との比較、そして具体的な実装方法までを網羅的に解説しました。
SSRは、優れたSEO効果や動的コンテンツへの対応力といった強力なメリットを持つ一方で、サーバーへの負荷やページ遷移時の遅延可能性といった考慮すべき側面も併せ持つ、トレードオフの存在する技術です。
これらの特性を深く理解することが、パフォーマンスとユーザー体験を最大化するアプリケーション開発に繋がります。