React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方
このチュートリアルでは、React Server Components(RSC)の仕組みを理解した上で、その知識を活かしてアプリにシンプルな閲覧数カウンターを実装していきます。すぐに実装に取り掛かりたい方は、「プロジェクトのセットアップ」までスキップしてください。
React Server Componentsを理解する
React Server Componentsがどのように動作するのかを理解するために、まずクライアントサイドレンダリング(CSR)とサーバーサイドレンダリング(SSR)について簡単におさらいしましょう。
クライアントサイドレンダリング(CSR)
CSRでは、レンダリング処理の大部分がクライアント側、つまりブラウザ上で行われます。
- ユーザーがサイトへのアクセスをリクエストする。
- サーバーがHTMLファイルとCSS・JSファイルへのリンクを返す。
- クライアントがJSリソースをダウンロードする。
- クライアントがコンテンツなしの状態でページをレンダリングする。
- クライアントがサーバーのAPIからデータを取得する。
- クライアントがデータを含んだ状態でページを再レンダリングする。

サーバーサイドレンダリング(SSR)
SSRでは、リクエストごとにサーバーが完全なHTMLを生成し、クライアントへ送信します。
- ユーザーがサイトへのアクセスをリクエストする。
- サーバーが実際のHTMLファイルをレンダリングする(まだインタラクティブではない)。
- クライアントがJSリソースをダウンロードする。
- クライアント側で
hydrateRoot()関数を使い、イベントリスナーやステートなどのReactコンポーネントのインタラクティブな機能を、サーバーが生成した既存のDOM要素に紐付けます。これをハイドレーション(hydration)と呼びます。 - クライアントがサーバーのAPIからデータを取得する。
- クライアントがデータを含んだ状態でページを再レンダリングする。

SSRのメリット
- SEOに強い: 検索エンジンのクローラーが完全にレンダリングされたHTMLを読み取れるため、SEOの面で有利です。
- 初期表示が速い: 完全なHTMLがサーバーから送信されるため、ユーザーは素早くページの内容を確認できます。
SSRのデメリット
- ページ遷移が遅い: 新しいページへのリクエストごとに、サーバーからHTMLを再取得する往復通信が必要になります。
- サーバー負荷の増大: レンダリング処理の多くをサーバーが担うため、多数のリクエストを処理するにはより多くのリソースが必要になる場合があります。
React Server Components(RSC)
RSCは、コンポーネントを完全にサーバー上でレンダリングできるようにするReactの機能です。
- サーバーがデータを取得する。
- サーバーがデータを含んだ状態でアプリをレンダリングする。
- クライアントがJSリソースをダウンロードする。
- クライアント側でハイドレーションが行われる。
注意: サーバーコンポーネントは完全にサーバー上でレンダリングされるため、JSバンドルには含まれず、ハイドレーションも行われません。つまり、手順3と4はクライアントコンポーネントに対してのみ発生します。

RSCのメリット
- 効率的なデータ処理: データフェッチがサーバー側、つまりデータベースの近くで行われるため、クライアント・サーバー間の往復通信が不要になり、パフォーマンスが向上します。
- クライアントのJavaScriptサイズ削減: RSCはクライアントにJavaScriptを一切送信しないため、バンドルサイズ全体が縮小し、パフォーマンスが改善します。
- 高速な初回ページロード: サーバー上でコンポーネントを事前レンダリングすることで、クライアントは完全にレンダリングされたHTMLをより速く受け取れ、First Contentful Paint(FCP)の時間が短縮されます。
- SEOの向上: コンテンツがサーバー側で完全にレンダリングされるため、検索エンジンがクロールやインデックスを行いやすくなります。
RSCの課題
- ステートとライフサイクル: RSCはサーバー上でレンダリングされるため、
useStateやuseEffectといったブラウザAPIにアクセスできません。 - クライアント側インタラクティブ性の欠如: RSCは動的な更新やインタラクティブな操作を想定していません。アプリのインタラクティブな部分にはクライアントコンポーネントを使用する必要があります。
React Server Componentsの使い方
Next.jsにおけるReact Server Components
Next.jsではデフォルトでServer Componentsが使用され、以下の3つの方法でレンダリングされます。
- 静的レンダリング(デフォルト): ルートはビルド時、またはデータ再検証後のバックグラウンドでレンダリングされます。
- 動的レンダリング: ルートはユーザーごとにリクエスト時にレンダリングされます。
- ストリーミング: UIがサーバーから段階的にレンダリングされます。
React Server Componentsを使うべき場面
次のような場合にRSCの使用が適しています:
- 頻繁に変更されない静的で非インタラクティブなコンテンツを扱う場合。
- クライアントへ送信するJavaScriptを減らしてパフォーマンスを向上させたい場合。
- アプリケーションにおいてSEOが重要な場合。
- 重いデータフェッチや計算処理をサーバーにオフロードしたい場合。
次のような場合にはRSCの使用を避けるべきです:
- コンポーネントにクライアント側のインタラクティブ性、ステート、フックが必要な場合。
- ブラウザAPIへのアクセスやリアルタイム更新が必要な場合。
- 複雑なクライアント側ロジックやユーザー固有のコンテンツを含むページを構築している場合。
よくある落とし穴
クライアントコンポーネントは完全にクライアント側でレンダリングされるわけではない
実際にはクライアントとサーバーの両方でレンダリングされます。「Client Components」という名称は、Server Componentsと区別するためにつけられたものです。
'use server'ディレクティブはServer Actions用
Next.jsはデフォルトでServer Componentsを使用するため、特別な指定は不要です。Client Componentsを使いたい場合は、'use client'ディレクティブを追加してサーバーモジュールとクライアントモジュールの境界を宣言します。一方、'use server'はServer Actionsのためのまったく別のディレクティブであり、本記事の範囲外となります。
プロジェクトのセットアップ
VercelのBlogテンプレートを使用します。
pnpm create next-app --example https://github.com/vercel/examples/tree/main/solutions/blog blog
ローカルでサンプルを実行して、見た目を確認してみましょう。
cd blog
pnpm dev
次に@upstash/redisをインストールします。
pnpm add @upstash/redis
環境設定
- Upstash Console → Redisにアクセスし、新しいデータベースを作成します。

- REST APIセクションまでスクロールし、
.envタブに切り替えて、環境変数をコピーします(次のステップで使用します)。

.envファイルを作成し、環境変数を貼り付けます。
UPSTASH_REDIS_REST_URL=<YOUR_URL>
UPSTASH_REDIS_REST_TOKEN=<YOUR_TOKEN>
Viewsコンポーネントのセットアップ
/app/components/views.tsxを作成します。
import { headers } from 'next/headers'
import { Redis } from "@upstash/redis"
const redis = Redis.fromEnv();
async function view(slug: string, ip: string) {
// IPアドレスをハッシュ化して匿名化する
const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(ip));
const hash = Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
// 閲覧数の重複を排除する
const newView = await redis.set(`deduplicate:${hash}:${slug}`, true, {
nx: true, // キーが存在しない場合のみセットする
ex: 24 * 60 * 60, // 24時間後にキーを失効させる
});
if (newView) {
await redis.incr(`pageviews:${slug}`); // 閲覧数をインクリメントする
}
}
export default async function Views({ slug }: { slug: string }) {
// ユーザーのIPアドレスを取得する
const header = headers()
const ip = (header.get('x-forwarded-for') ?? '127.0.0.1').split(',')[0]
// 閲覧数をインクリメントする
await view(slug, ip)
// 閲覧数を取得する
const views = await redis.get<number>(`pageviews:${slug}`) || 0
return (
<p className="text-sm text-neutral-600 dark:text-neutral-400">
{views} views
</p>
)
}
Viewsコンポーネントのインポートと表示
/app/blog/[slug]/page.tsxを編集します。
...
import Views from 'app/components/views'
...
<div className="flex justify-between items-center mt-2 mb-8 text-sm">
<p className="text-sm text-neutral-600 dark:text-neutral-400">
{formatDate(post.metadata.publishedAt)}
</p>
<Views slug={post.slug} />
</div>
...
https://localhost:3000/blog/vim にアクセスすると、閲覧数カウンターの動作を確認できます。

クライアントコンポーネントでの実装との比較
クライアントコンポーネントを使った実装については、ブログ記事「Adding a View Counter to your Next.js Blog」をご覧ください。
前述のメリットに加えて、React Server Componentsを使用することで以下の利点があります。
- 別途APIを用意する必要がなくなった。
- 型チェックの恩恵を受けながら、コンポーネントとそのロジックをシンプルに一元管理できるようになった。
デプロイ
以下のコマンドでサイトをVercelにデプロイできます。
vercel
まとめ
Server ComponentsとClient Componentsそれぞれの強みを組み合わせることで、アプリケーションにおけるパフォーマンスとインタラクティブ性の最適なバランスを実現できます。このガイドが、React Server Componentsを効果的に活用すべき場面を見極める判断材料になれば幸いです。
-
Redis HKEYS コマンド – ハッシュに含まれるすべてのフィールド名を取得する方法
このチュートリアルでは、redis-cli で HKEYS コマンドを使って、指定したキーに保存されているハッシュ値に含まれるすべてのフィールド名を取得する方法を解説します。 HKEYS コマンドを実行すると、ハッシュ内のすべてのフィールド(キー)の一覧を配列として取得できます。なお、指定したキーが存在しない場合は空のリストが返され、キーは存在するものの保存されている値がハッシュ型ではない場合はエラーが返される点に注意してください。 構文 :- redis host:post> HKEYS <keyname> 戻り値 :- - (array) reply:ハッシュに含まれ
-
加速するデジタルトランスフォーメーション──あなたのデータレイヤーは準備できていますか?
この10年間、デジタルトランスフォーメーション(DX)については数え切れないほど語られてきました。Redisでは、変革を可能にする一連の技術レイヤー(クラウド、マイクロサービス、コンテナ、NoSQLデータベース)と、企業が変革の旅をどれだけ速く、成功裏に推進できるかとの間に、直接的な関係があると捉えています。 しかし、この関係をどうやって実行可能なインサイトへと定量化すればよいのでしょうか? そこで登場するのが「Digital Transformation Index(DTI:デジタルトランスフォーメーション指数)」です。まず、上述の4つの技術の採用状況を組み合わせて、企業が変革の道程のどこ