Next.js + TypescriptでミニCMSを作る（3. Next.jsのコンポーネントとデータ取得）

# 実装前にNext.jsの特徴をまとめます
:::note info
[前回](https://qiita.com/AndanteSysDes/items/b2bd26db36a08b090c2e)の続きです。
:::

コーディングを進めるまえに、どう実装するか考察していきます。

# フルスタックなNext.js
Next.jsはフルスタックなReactフレームワークです。すなわち、クライアント側（要はブラウザ）で動く機能（フロントエンド）と、サーバー側で動く機能（バックエンド）のどちらも持ち合わせています。
この各環境には独自の機能と制約がありますので、これらの概念と特徴を整理しておくのが重要です。双方のメリット・デメリットを簡単にまとめようと思います。

:::note info
本記事ではNext.js 14とApp Routerを使用しております。Next.js 13以前のPage Routerでは取り扱いが異なりますので、ご注意ください。
:::

## コンポーネントについて
直訳すると、部品・構成要素といった意味になります。
React履修済みの方は御存知の通り、画面に表示されるパーツで、必要なデータや処理をひとまとまりにしオブジェクトとして扱うものです。
例えば今回作るレビュー一覧画面だと、カードのように表示されているパーツがコンポーネントとしてわかりやすいと思います。ワイヤーフレームでも表されている通り、複数のカードが表示されています。
これらは一つのコンポーネントとしてまとめることで、重複する記述を省いたり、別ページで同じスタイルのまま使い回したりすることができます。
Next.jsのコンポーネントは、ソースコードがレンダリングされる場所とタイミングが異なる、２種類のコンポーネントがあります。

### サーバーコンポーネント
サーバー上でレンダリングされるコンポーネント。Next.jsでコンポーネントを作成する際、デフォルトでサーバーコンポーネントになります。データ取得速度やセキュリティ、SEO面で有利で、パフォーマンスが高いです。
一般的に、サーバーコンポーネントは静的コンテンツやルーティング、データ取得で使われることが多いようです。
**前回色々と設定したPrismaは、このサーバーコンポーネントの上で動かさねばなりません。**

ただし、サーバーコンポーネントはページがクライアントに送られる前にレンダリングされるため、**データ操作が頻繁に行われ、表示に即時性が求められる場合には向きません。**

### クライアントコンポーネント
サーバーコンポーネントに対してクライアントコンポーネントは、レンダリングがクライアント側で行われます。そのため、サーバーサイドのデータにアクセスすることはできません。
一般的に、**インタラクティブなUI**が求められる場合に利用されます。React履修者にはおなじみのState, Effectや, Event listenerを使えるのはクライアントコンポーネントとなります。
クライアントコンポーネントにしたいけど、DB操作などサーバー機能も使いたい！という場面も多いかと思います。
その場合は、**サーバーコンポーネントで定義した関数(サーバーアクション)をPropsとして親から子のクライアントコンポーネントに渡す、または別ファイルからimportするというテクニックを使います。**
実際に実装を見ていく中でまた説明していきますね。


↓サーバーコンポーネントとクライアントコンポーネントの公式比較

https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns

## Next.jsにおけるデータ取得方法について
Next.jsは、データを取得する方法がいくつかあります。公式はなるべくサーバーコンポーネントを用いてサーバー側からデータ取得することを勧めています。

### `fetch`を使う
Next.jsにて機能拡張された`fetch`WebAPIを使ってアクセスポイントのURLに対してfetchをする方法です。`async`/`await`とあわせて使用します。
サーバーコンポーネント、[ルートハンドラ](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)、[サーバーアクション](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations)で使えます。
最も一般的なデータ取得方法かと思います。今回のケースでは状況に応じて活用していきます。

```tsx: sample.tsx
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のクライアントを使ってデータを取得します。

```tsx: sample.tsx
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

async function getData() {
  const allUsers = await prisma.user.findMany()
  return allUsers
}

//以下割愛
```

### クライアントコンポーネントからルードハンドラを使ってデータを取得する
クライアント側でどうしてもデータを取得する必要がある場合は、クライアントからルートハンドラを呼ぶことができます。
クライアントコンポーネントでは`async`/`await`は使えませんので、Promiseを作成します。

```tsx: sample.tsx
'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`などのリクエストに対応できます。

```ts: app/api/sample/route.ts
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 トークンなどの機密情報をクライアントに公開したくない場合に便利です。

https://nextjs.org/docs/app/building-your-application/routing/route-handlers

### クライアントコンポーネントからサードパーティライブラリを利用する
パターンとしてはありますが、Prismaは仕様上これを使うことができませんので説明は割愛します。

# 次回
これらを踏まえて、実装に入っていきます。
まずはレビュー一覧画面からです。