Nextjs getServerSideProps, getStaticProps, Client-side Fetchingの使い分け


# 目次
- getStaticProps
- getServerSideProps
- Client-side Fetching

### 注意点
*以後、引用はnextjs documentをdeeplで翻訳をかけたものになります。
それでは説明していきます。
*この記事はNext.js pages routerの情報になります。

## getStaticProps
サンプルコードをまず貼りたいと思います。
```ts
import type { InferGetStaticPropsType, GetStaticProps } from 'next'
 
type Repo = {
  name: string
  stargazers_count: number
}
 
export const getStaticProps: GetStaticProps<{
  repo: Repo
}> = async () => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const repo = await res.json()
  return { props: { repo } }
}
 
export default function Page({
  repo,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  return repo.stargazers_count
}
```
getStaticPropsについてはnext buildされた時にgetStaticPropsの中身を実行し、
HTML, JSONを生成し、CDNによってキャッシュすることができます。

getStaticPropsの注意点として以下が記述されています。
> レンダリングタイプに関係なく、すべてのpropsはページコンポーネントに渡され、最初のHTMLでクライアントサイドで見ることができることに注意してください。これはページが正しくハイドレイトされるようにするためです。クライアントで利用可能であってはならない機密情報をpropsで渡さないように注意してください。

なのでsecretなデータはpropsで渡さないように注意しましょう。
また、getStaticPropsには[revalidate](https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation)というプロパティがあります。これを指定することで、ISRというdocumentを見てみましょう。
> 再有効化時間を60に設定すると、すべての訪問者は1分間、あなたのサイトの同じ生成バージョンを見ることになります。キャッシュを無効にする唯一の方法は、1分経過後にそのページを訪問する誰かからです。

なので、revalidaを指定すると指定秒数後にアクセスした人がいたときにOn-Demandでページ更新が可能になります。こちらはISR(Incremental Static Regeneration)と呼ばれています。

またgetStaticPropsはサーバーサイドで実行されるため、ブラウザ用のJSバンドルにも含まれないので、ブラウザに送信されることなく直接データベースクエリを書くことができます。


## getServerSideProps
> ページからgetServerSideProps（サーバーサイドレンダリング）という関数をエクスポートすると、Next.jsはリクエストごとに、getServerSidePropsが返すデータを使ってこのページをプリレンダリングします。

documentの冒頭に上記が記述されており、リクエストごとにgetServerSidePropsを使用して、プリレンダリングくれるようです。
getStaticPropsはビルド時のみ(revalidateとつけるとon-demand)でgetServerSidePropsはリクエストごとにコードが実行されます。

サンプルコードは以下になります。
```ts
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
 
type Repo = {
  name: string
  stargazers_count: number
}
 
export const getServerSideProps: GetServerSideProps<{
  repo: Repo
}> = async () => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const repo = await res.json()
  return { props: { repo } }
}
 
export default function Page({
  repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return repo.stargazers_count
}
```

getServerSidePropsも同様にpropsにsecret情報を含めないようにしましょう。
それではdocumentを見てみましょう。
> getServerSideProps を使用するのは、リクエスト時にデータを取得しなければならないページをレンダリングする必要がある場合だけです。これは、データの性質やリクエストのプロパティ(認可ヘッダや地理的な位置など)に起因する可能性があります。getServerSideProps を使用したページは、リクエスト時にサーバ側でレンダリングされ、キャッシュ制御ヘッダが設定されている場合にのみキャッシュされます。

- getStaticPropsはリクエスト時にデータ取得をしなければいけない時に使用すること
- キャッシュ制御ヘッダが設定されている場合キャッシュが有効になること
上記の二つが主に言及されています。

キャッシュ制御ヘッダについてみていきましょう。
```ts
// This value is considered fresh for ten seconds (s-maxage=10).
// If a request is repeated within the next 10 seconds, the previously
// cached value will still be fresh. If the request is repeated before 59 seconds,
// the cached value will be stale but still render (stale-while-revalidate=59).
//
// In the background, a revalidation request will be made to populate the cache
// with a fresh value. If you refresh the page, you will see the new value.
export async function getServerSideProps({ req, res }) {
  res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  )
 
  return {
    props: {},
  }
}
```
上記がサンプルコードで以下のCache-Controlを使用してキャッシュについてのコントロールが可能です。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

ここではここでは[public](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#:~:text=information%20to%20leak.-,public,-The%20public%20response)で共有キャッシュ格納、[s-maxage](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#:~:text=Age%3A%20100-,s%2Dmaxage,-The%20s%2Dmaxage)の指定でキャッシュが古くない時間、[state-while-revalidate](https://web.dev/stale-while-revalidate/)が設定されています。

stale-while-revaidateに触れながら、サンプルコードの説明をしたいと思います。
- s-maxage=10なので10秒間はキャッシュが新鮮なものと判断される
- 10秒~69秒は、キャッシュは古いと判断されてrevalidateが走りキャッシュ格納
- 69秒以降はブラウザのリクエスト、revalidateがどちらも走る
という形になります。

ですが、以下のような記述がdocumentにあります。
> ページがgetServerSidePropsまたはgetInitialPropsを使用する場合、キャッシュできないレスポンスを誤ってキャッシュすることを防ぐために、next startによって設定されたデフォルトのCache-Controlヘッダを使用します。getServerSidePropsを使用する際に異なるキャッシュ動作をさせたい場合は、上記のように関数内でres.setHeader('Cache-Control', 'value_you_prefer')を使用します。

なので、以下のようになります。
- getServerSidePropsはデフォルトでCache-Controlが設定されている
- 自分でキャッシュ設定したい場合は可能

## Client-side Fetching
nextjsのdocumentで以下の説明があります。
> クライアント側のデータ取得は、ページがSEOインデックスを必要としない場合、データを事前にレンダリングする必要がない場合、またはページのコンテンツが頻繁に更新される必要がある場合に便利です。サーバー側レンダリング API とは異なり、クライアント側データ取得はコンポーネント レベルで使用できます。

情報整理すると以下です。
- SEOを考慮しない場合
- データを事前にレンダリングが必要ない場合
- ページのコンテンツが頻繁に更新する必要がある場合

というように上記三つの条件下で使用されることが推奨されています。
補足情報
- コンポーネントレベルでデータ取得が可能

ただ、注意点として、以下のことが記述されています。
> クライアントサイドのデータ・フェッチを使用すると、アプリケーションのパフォーマンスやページのロード速度に影響を与える可能性があることに注意することが重要です。

これはデータフェッチがコンポーネント、ページのマウント時にされるのでキャッシュされないことからです。useEffectを使用するとここで言われていること通り、キャッシュされないデメリットがあります。

ここでvercelはvercelチームが開発している[SWR](https://swr.vercel.app/ja)の使用を強く推奨しています。
特徴としては、キャッシュ、再バリデーション、フォーカストラッキング、インターバルでの再フェッチなどを処理です。
SWRはキャッシュを保持し、キャッシュが古くなったら自動でrevalidateしてキャッシュの更新を行ってくれるという優れものです。

SWR DOCSはこちら[SWR DOCS](https://swr.vercel.app/docs/getting-started)
nextjs documentからのサンプルコードは以下
```js
import useSWR from 'swr'
 
const fetcher = (...args) => fetch(...args).then((res) => res.json())
 
function Profile() {
  const { data, error } = useSWR('/api/profile-data', fetcher)
 
  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>
 
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  )
}
```
swrについては長くなりすぎるため、記事を分けて説明していきます。

### それぞれ使うタイミングと用途の振り返り
getStaticProps
- ビルド時からデータ取得の値が変わらない場合使用(ただ、revalidateでon-demandで取得可能
getServerSideProps
- リクエスト時にプリレンダリングする必要がある時に使用する。

Client-side Fetching
- SEOを考慮しない場合
- データを事前にレンダリングが必要ない場合
- ページのコンテンツが頻繁に更新する必要がある場合

上記のようになります。

ここまで読んでいただきありがとうございました。

参照

https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props

https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props

https://nextjs.org/docs/pages/building-your-application/data-fetching/client-side