実装前にNext.jsの特徴をまとめます
前回の続きです。
コーディングを進めるまえに、どう実装するか考察していきます。
フルスタックなNext.js
Next.jsはフルスタックなReactフレームワークです。すなわち、クライアント側(要はブラウザ)で動く機能(フロントエンド)と、サーバー側で動く機能(バックエンド)のどちらも持ち合わせています。
この各環境には独自の機能と制約がありますので、これらの概念と特徴を整理しておくのが重要です。双方のメリット・デメリットを簡単にまとめようと思います。
本記事ではNext.js 14とApp Routerを使用しております。Next.js 13以前のPage Routerでは取り扱いが異なりますので、ご注意ください。
コンポーネントについて
直訳すると、部品・構成要素といった意味になります。
React履修済みの方は御存知の通り、画面に表示されるパーツで、必要なデータや処理をひとまとまりにしオブジェクトとして扱うものです。
例えば今回作るレビュー一覧画面だと、カードのように表示されているパーツがコンポーネントとしてわかりやすいと思います。ワイヤーフレームでも表されている通り、複数のカードが表示されています。
これらは一つのコンポーネントとしてまとめることで、重複する記述を省いたり、別ページで同じスタイルのまま使い回したりすることができます。
Next.jsのコンポーネントは、ソースコードがレンダリングされる場所とタイミングが異なる、2種類のコンポーネントがあります。
サーバーコンポーネント
サーバー上でレンダリングされるコンポーネント。Next.jsでコンポーネントを作成する際、デフォルトでサーバーコンポーネントになります。データ取得速度やセキュリティ、SEO面で有利で、パフォーマンスが高いです。
一般的に、サーバーコンポーネントは静的コンテンツやルーティング、データ取得で使われることが多いようです。
前回色々と設定したPrismaは、このサーバーコンポーネントの上で動かさねばなりません。
ただし、サーバーコンポーネントはページがクライアントに送られる前にレンダリングされるため、データ操作が頻繁に行われ、表示に即時性が求められる場合には向きません。
クライアントコンポーネント
サーバーコンポーネントに対してクライアントコンポーネントは、レンダリングがクライアント側で行われます。そのため、サーバーサイドのデータにアクセスすることはできません。
一般的に、インタラクティブなUIが求められる場合に利用されます。React履修者にはおなじみのState, Effectや, Event listenerを使えるのはクライアントコンポーネントとなります。
クライアントコンポーネントにしたいけど、DB操作などサーバー機能も使いたい!という場面も多いかと思います。
その場合は、サーバーコンポーネントで定義した関数(サーバーアクション)をPropsとして親から子のクライアントコンポーネントに渡す、または別ファイルからimportするというテクニックを使います。
実際に実装を見ていく中でまた説明していきますね。
↓サーバーコンポーネントとクライアントコンポーネントの公式比較
Next.jsにおけるデータ取得方法について
Next.jsは、データを取得する方法がいくつかあります。公式はなるべくサーバーコンポーネントを用いてサーバー側からデータ取得することを勧めています。
fetch
を使う
Next.jsにて機能拡張されたfetch
WebAPIを使ってアクセスポイントのURLに対してfetchをする方法です。async
/await
とあわせて使用します。
サーバーコンポーネント、ルートハンドラ、サーバーアクションで使えます。
最も一般的なデータ取得方法かと思います。今回のケースでは状況に応じて活用していきます。
async function getData() {
const res = await fetch('https://api.example.com/...')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
サーバーコンポーネントからサードパーティライブラリを利用する
今回はPrisma ORMを利用するので、こちらのパターンを多用することになります。
ORMのクライアントを使ってデータを取得します。
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function getData() {
const allUsers = await prisma.user.findMany()
return allUsers
}
//以下割愛
クライアントコンポーネントからルードハンドラを使ってデータを取得する
クライアント側でどうしてもデータを取得する必要がある場合は、クライアントからルートハンドラを呼ぶことができます。
クライアントコンポーネントではasync
/await
は使えませんので、Promiseを作成します。
'use client'
export default function Page() {
const getData = async () => {
fetch('/api/sample').then((data) => data.json())
}
const data = getData()
return <main></main>
}
ルートハンドラ
app/api/
配下にroute.ts
を置くことで、Webリクエスト/レスポンスを使ったカスタムハンドラを作成することができます。
この機能によって、Next.js内部で簡易APIサーバを作り、GET
やPOST
などのリクエストに対応できます。
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const product = await res.json()
return Response.json({ product })
}
ルート ハンドラーはサーバー上で実行され、データをクライアントに返します。これは、API トークンなどの機密情報をクライアントに公開したくない場合に便利です。
クライアントコンポーネントからサードパーティライブラリを利用する
パターンとしてはありますが、Prismaは仕様上これを使うことができませんので説明は割愛します。
次回
これらを踏まえて、実装に入っていきます。
まずはレビュー一覧画面からです。