Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

このチュートリアルでは、React Server Components(RSC)の仕組みを理解した上で、その知識を活かしてアプリにシンプルな閲覧数カウンターを実装していきます。すぐに実装に取り掛かりたい方は、「プロジェクトのセットアップ」までスキップしてください。

React Server Componentsを理解する

React Server Componentsがどのように動作するのかを理解するために、まずクライアントサイドレンダリング(CSR)とサーバーサイドレンダリング(SSR)について簡単におさらいしましょう。

クライアントサイドレンダリング(CSR)

CSRでは、レンダリング処理の大部分がクライアント側、つまりブラウザ上で行われます。

  1. ユーザーがサイトへのアクセスをリクエストする。
  2. サーバーがHTMLファイルとCSS・JSファイルへのリンクを返す。
  3. クライアントがJSリソースをダウンロードする。
  4. クライアントがコンテンツなしの状態でページをレンダリングする。
  5. クライアントがサーバーのAPIからデータを取得する。
  6. クライアントがデータを含んだ状態でページを再レンダリングする。

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

サーバーサイドレンダリング(SSR)

SSRでは、リクエストごとにサーバーが完全なHTMLを生成し、クライアントへ送信します。

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

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

SSRのメリット

  • SEOに強い: 検索エンジンのクローラーが完全にレンダリングされたHTMLを読み取れるため、SEOの面で有利です。
  • 初期表示が速い: 完全なHTMLがサーバーから送信されるため、ユーザーは素早くページの内容を確認できます。

SSRのデメリット

  • ページ遷移が遅い: 新しいページへのリクエストごとに、サーバーからHTMLを再取得する往復通信が必要になります。
  • サーバー負荷の増大: レンダリング処理の多くをサーバーが担うため、多数のリクエストを処理するにはより多くのリソースが必要になる場合があります。

React Server Components(RSC)

RSCは、コンポーネントを完全にサーバー上でレンダリングできるようにするReactの機能です。

  1. サーバーがデータを取得する。
  2. サーバーがデータを含んだ状態でアプリをレンダリングする。
  3. クライアントがJSリソースをダウンロードする。
  4. クライアント側でハイドレーションが行われる。

注意: サーバーコンポーネントは完全にサーバー上でレンダリングされるため、JSバンドルには含まれず、ハイドレーションも行われません。つまり、手順3と4はクライアントコンポーネントに対してのみ発生します。

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

RSCのメリット

  • 効率的なデータ処理: データフェッチがサーバー側、つまりデータベースの近くで行われるため、クライアント・サーバー間の往復通信が不要になり、パフォーマンスが向上します。
  • クライアントのJavaScriptサイズ削減: RSCはクライアントにJavaScriptを一切送信しないため、バンドルサイズ全体が縮小し、パフォーマンスが改善します。
  • 高速な初回ページロード: サーバー上でコンポーネントを事前レンダリングすることで、クライアントは完全にレンダリングされたHTMLをより速く受け取れ、First Contentful Paint(FCP)の時間が短縮されます。
  • SEOの向上: コンテンツがサーバー側で完全にレンダリングされるため、検索エンジンがクロールやインデックスを行いやすくなります。

RSCの課題

  • ステートとライフサイクル: RSCはサーバー上でレンダリングされるため、useStateuseEffectといったブラウザ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

環境設定

  1. Upstash Console → Redisにアクセスし、新しいデータベースを作成します。

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

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

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

  1. .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 にアクセスすると、閲覧数カウンターの動作を確認できます。

React Server Componentsで実装するリアルタイム閲覧数カウンターの作り方

クライアントコンポーネントでの実装との比較

クライアントコンポーネントを使った実装については、ブログ記事「Adding a View Counter to your Next.js Blog」をご覧ください。

前述のメリットに加えて、React Server Componentsを使用することで以下の利点があります。

  • 別途APIを用意する必要がなくなった。
  • 型チェックの恩恵を受けながら、コンポーネントとそのロジックをシンプルに一元管理できるようになった。

デプロイ

以下のコマンドでサイトをVercelにデプロイできます。

vercel

まとめ

Server ComponentsとClient Componentsそれぞれの強みを組み合わせることで、アプリケーションにおけるパフォーマンスとインタラクティブ性の最適なバランスを実現できます。このガイドが、React Server Componentsを効果的に活用すべき場面を見極める判断材料になれば幸いです。

  1. Redis HKEYS コマンド – ハッシュに含まれるすべてのフィールド名を取得する方法

    このチュートリアルでは、redis-cli で HKEYS コマンドを使って、指定したキーに保存されているハッシュ値に含まれるすべてのフィールド名を取得する方法を解説します。 HKEYS コマンドを実行すると、ハッシュ内のすべてのフィールド(キー)の一覧を配列として取得できます。なお、指定したキーが存在しない場合は空のリストが返され、キーは存在するものの保存されている値がハッシュ型ではない場合はエラーが返される点に注意してください。 構文 :- redis host:post> HKEYS <keyname> 戻り値 :- - (array) reply:ハッシュに含まれ

  2. 加速するデジタルトランスフォーメーション──あなたのデータレイヤーは準備できていますか?

    この10年間、デジタルトランスフォーメーション(DX)については数え切れないほど語られてきました。Redisでは、変革を可能にする一連の技術レイヤー(クラウド、マイクロサービス、コンテナ、NoSQLデータベース)と、企業が変革の旅をどれだけ速く、成功裏に推進できるかとの間に、直接的な関係があると捉えています。 しかし、この関係をどうやって実行可能なインサイトへと定量化すればよいのでしょうか? そこで登場するのが「Digital Transformation Index(DTI:デジタルトランスフォーメーション指数)」です。まず、上述の4つの技術の採用状況を組み合わせて、企業が変革の道程のどこ