【Next.js】入門してみたまとめ ✍🏻

# Next.jsとは

![スクリーンショット 2023-06-18 16.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/90a30a63-bf80-3cb7-8518-c60f990209c6.png)

Vercel社が開発したReact開発のためのフレームワーク🚀
高速なWebアプリケーション作成のための様々な機能を提供しています。
ルーティングや開発環境などが簡単に構築、設定でき、SSR（Server Side Rendering）やSSG（Static Site Generation）、ISR（Incremental Static Regeneration）といったサーバー側のレンダリング機能がデフォルトで備えられてるところが特長です。

:::note
**React**
UIを構築するための機能を提供するライブラリ

**Next.js**
React開発のための機能を提供するフレームワーク
:::

## Next.jsの主な機能

* 複数のレンダリング方法(SSR、SG、ISG)
* ファイルベースルーティング(ダイナミックルート)
* APIの作成(API Routes)
* デベロッパーに優しい開発環境(ゼロコンフィグ)


## プリレンダリングとは

プリレンダリングとは、サーバーサイドでページのHTMLを事前に生成し、レンダリングする機能です。
ブラウザへの初期表示速度や、検索エンジン最適化（SEO）を大きく向上させます。
Next.jsでは、このプリレンダリングを下記2つの方法で提供します。

### SSG (Static Site Generation)

Static Site Generationの頭文字をとった造語で、静的生成を意味します。
プリレンダリングのタイミングはビルド時（next build）です。

### SSR（Server Side Rendering）

Server Side Renderingの頭文字をとった造語で、サーバーサイドレンダリングという言葉の通り、
サーバー側でレンダリングしてくれます。


## Next.jsのインストール

インストールにはいくつか方法がありますので、都度プロジェクトに合わせて行ってください。
下記は一例です。

`create-next-app プロジェクト名`
`cd プロジェクト名`
`npm run dev`
上記でローカルサーバーが立ち上がります。

## ディレクトリ構成

上記などの方法でインストールが完了するとプロジェクトの雛形が作成されています。
インストール直後のデフォルトではルートディレクトリ配下に下記ディレクトリなどが配置されています。

**/pages**
pagesディレクトリ配下記載したコンポーネントファイルが一つのページに対応することになります。
pagesディレクトリにファイルを追加すれば、自動的にルーティングが作成されます。

```
pages/index.js → /
pages/about/index.js → /about
```

**/styles**
cssファイルを格納します。

**/public**
画像ファイルやフォントファイル、アイコンファイルなど静的ファイルを格納します。

:::note
デフォルトのままのフォルダ構成で開発しても問題ないが、主にpagesフォルダとstylesフォルダにコードを加えていくことになると思うので、ルートディレクトリに`src`フォルダを作成して、pagesフォルダ・stylesフォルダを`src`フォルダに格納して開発したりします。
:::

## Next.jsにおけるルーティングの方法

Next.jsの場合は、/pages内がルーディングの対象になり、ファイルまでのパスがそのままurlになります。
例えば/pages配下にindex.jsファイルを作成した場合、`http://localhost:3000/`などルートパスに対して作成したindex.js内の内容が返却されます。

:::note
**ルーティングとは**
URLでリクエストを送信した際に、どのプログラムを実行するかの紐付けです。
:::

例1)
pages/
　└ index.js

```jsx:pages/index.js
export default function Home() {
  return <h1>/pages/index.js</h1>;
}
```

`npm run dev`でローカルサーバー立ち上げると下記内容がレンダリングされます。
![スクリーンショット 2023-06-11 15.28.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/3115030b-d5eb-683a-6556-1d2fe15e5a17.png)

例2)
pages/
　└ index.js
　└ blog/
　　└ index.js

```jsx:pages/blog/index.js
export default function Blog() {
  return <h1>/blog/index.js</h1>;
}
```

`http://localhost:3000/blog`をブラウザurlに入力すると上記ファイルが返却されるので下記画面が表示されます。
![スクリーンショット 2023-06-11 15.42.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/6863aac0-6deb-bc6b-f08f-f17da9301016.png)

## 動的なルーティング

前項では、「/pages内がルーディングの対象になり、ファイルまでのパスがそのままurlになります。」と説明をしました。
ですが例えばブログページで考えた場合に、投稿するごとに増えるurlに対して、`/pages/blog/1`、`/pages/blog/2`のようにファイルを作成していくわけではありません。
このようなケースの時に使用するのが、動的なルーディング、**ダイナミックルーティング**です💡
ダイナミックルーティングを用いることによって、`/pages/blog/1`の1の部分など、urlの一部を動的な値に変更することができ、jsファイルを一つだけ作成すればいいことになります。

例)
pages/
　└ index.js
　└ blog/
　　└ **[number].js**  ← [number]部分がダイナミックルーティングになり、この値がjs内で使用可能になる📌

```jsx:pages/blog/[number].js
export default function number() {
  return <h1>blog/[number].js</h1>;
}
```
`http://localhost:3000/blog/1`、`http://localhost:3000/blog/2`などのurlを指定すると`[number].js`ファイルの中身が返却されています。
![スクリーンショット 2023-06-11 17.26.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/339859ed-e55a-e96d-6887-b9e3ce3b5797.png)

フォルダにもダイナミックルーティングを使用できます。

例)
pages/
　└ index.js
　└ blog/
　　└ [number].js
　└ **[name]/**
　　└ setting.js

`http://localhost:3000/sato/setting`、`http://localhost:3000/yamada/setting`などでアクセスしても同じように`setting.js`の中身が返却され下記画面がレンダリングされます。
![スクリーンショット 2023-06-11 17.32.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/389c0301-f8c5-3ceb-a3ea-5b1da727aeda.png)

ちなみに上記のようなディレクトリ構造で、`http://localhost:3000/blog/setting`にアクセスした場合、`/pages/blog/[number].js`の中身が返却されます。
ダイナミックルーティングを行っている同階層に固定パスがあった場合は、固定パスが優先されるとのことです✍🏻


## 動的に変わるパスの値を取得する方法

動的に変わるパスを取得する方法は主に二つです。

1. `getServerSideProps()`の使用
1. Reactフックである`useRouter`の使用

### getServerSideProps()

`getServerSideProps()`とはSSR(サーバーサイドレンダリング)時の、関数コンポーネントの`props`を定義する際に使用します。


例)
```jsx:pages/[name]/setting.js
export default function Settinh({ hello }) {
  return <h1>{hello}</h1>;
}

export async function getServerSideProps() {
  return {
    props: {
      hello: "こんにちは",
    },
  };
}

```

上記、関数コンポーネントは下記のようにレンダリングされ、`props`に設定した値が出力されています。
![スクリーンショット 2023-06-17 8.09.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/d16cf430-df23-d2a1-20ca-8f0a6cc398cd.png)

この`getServerSideProps()`は引数に`context`を取ることができ、その`context`の一つのプロパティである`query`の値に動的なパスを取得できます。

:::note
**`context`とは**
`getServerSideProps()`に渡されるオブジェクトであり、以下のようなプロパティを持っています。

* `context.params`: URL パラメータやダイナミックルーティングによって受け取ったパラメータが含まれるオブジェクトです。
* `context.query`: クエリパラメータが含まれるオブジェクトです。
* `context.req`: Node.js のリクエストオブジェクトです。ヘッダーや Cookie などの情報にアクセスできます。
* `context.res`: Node.js のレスポンスオブジェクトです。レスポンスに関する情報を設定したり、リダイレクトを行ったりできます。
:::

下記のように`context.query`をconsoleし、`http://localhost:3000/test/setting`にアクセスすると、`query.name`プロパティに動的なパスが格納されていることがわかります。

例)
```jsx:pages/[name]/setting.js
export default function Setting({ hello }) {
  return <h1>{hello}</h1>;
}

export async function getServerSideProps(context) {
  console.log(context.query); // → query: { name: 'test' }
  return {
    props: {
      hello: "こんにちは",
    },
  };
}
```

この`context.query`を`props`として渡すことによって動的なパスを関数コンポーネント内で使用できます🎉

例)
```jsx:pages/[name]/setting.js
export default function Setting({ hello, query }) {
  return (
    <>
      <h1>{hello}</h1>
      <h2>{query.name}</h2>
    </>
  );
}

export async function getServerSideProps(context) {
  return {
    props: {
      hello: "こんにちは",
      query: context.query, // 動的パス情報をqueryプロップスに格納
    },
  };
}
```

`http://localhost:3000/test/setting`にアクセスすると下記レンダリングがされています。
![スクリーンショット 2023-06-17 8.33.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/cdfbc87d-e387-4c51-2fd1-4c9f4bbadb23.png)

### `useRouter`フック

動的パス取得のもう一つの方法として`useRouter`フックの使用も可能です。
`useRouter`とはNext.jsで提供されるReactフックの一つで現在のルートやURLパラメータ、クエリパラメータなどのプロパティを持つオブジェクトを返却する関数になります✍🏻
返却されるオブジェクトを格納する定数名はなんでもよいですが、慣例的に`router`とすることが多いと思います。
`next/router`モジュールからインポートして使用します。
下記のようなコードで動的パスを取得し、レンダリングできます。

例)
```jsx:pages/[name]/setting.js
import { useRouter } from "next/router";

export default function Setting() {
  const router = useRouter(); // `query.name`などのプロパティを持つオブジェクトの格納
  return (
    <>
      <h1>{router.query.name}</h1>
    </>
  );
}

```

## useRouterを使って画面遷移させる方法

`useRouter`フックは様々なプロパティを持つオブジェクトを返却すると先述しました。
そのオブジェクトはプロパティだけではなく、様々なメソッドも含まれています。
その一つである'push()'は画面遷移させるメソッドです。
構文は下記のとおりです。
`router.push("遷移先URL","urlに表示させたいurl",オプション)`
第二引数以降は省略可能です。

https://nextjs-ja-translation-docs.vercel.app/docs/api-reference/next/router

例)
```jsx:pages/[name]/setting.js
import { useRouter } from "next/router";
export default function Setting() {
  const router = useRouter();
  const clickHandler = () => {
    router.push("/"); // 引数に渡したurlに遷移する
  };
  return (
    <>
      <h1>{router.query.name}</h1>
      <button onClick={clickHandler}>トップへ</button>
    </>
  );
}

```

### replaceメソッド

`replace()`というメソッドもあり、こちらも同様に画面遷移させるメソッドです。
基本的に`push()`と同じような構文で使用できますが、`push()`と違って、履歴に追加せず画面処理を行います。
遷移後、ブラウザバックを押した際に、遷移元の前のページを開くような挙動になります。
他にも様々なメソッドがありますが、丁寧に解説された記事がありましたのでここでは省略します。

https://qiita.com/cheez921/items/6856c2c72bb2b7cd9aab

##  Linkを使った画面遷移

Linkコンポーネントを使用することで、リンクへ遷移させることもできます。

例)

```jsx:pages/index.js
import Link from "next/link"; // Linkコンポーネントimport

export default function Home() {
  return (
    <>
      <h1>Home</h1>
      <Link href="/blog/1">
        <a>Blogへ</a>
      </Link>
    </>
  );
}
```

`router.push()`と同様に、as属性を用いることによって、ブラウザのURLバーに表示されるパスを指定もできます。

```jsx:pages/index.js
import Link from "next/link"; 

export default function Home() {
  return (
    <>
      <h1>Home</h1>
      <Link href="/blog/1" as="test-url">
        <a>Blogへ</a>
      </Link>
    </>
  );
}
```

href属性はオブジェクト形式で記述も可能で、クエリーパラメーターの指定もできます。
下記のようなオブジェクトを渡せば「Blogへ」リンククリックすると`/blog/1?key=value`へ遷移します。

```jsx:pages/index.js
import Link from "next/link";

export default function Home() {
  return (
    <>
      <h1>Home</h1>
      <Link
        href={{
          pathname: "/blog/1",
          query: {
            key: "value",
          },
        }}
      >
        <a>Blogへ</a>
      </Link>
    </>
  );
}

```

ちなみに`<a href="/blog/1">Blogへ</a>`のように普通にaタグも使えますが、Linkコンポーネントを使用した遷移と違ってリロードが発生する画面遷移になります📡

## シングルコンポーネントで複数画面を作成する方法

`router.push()`でクエリパラメーターを受け渡しして、一つのコンポーネントでも複数ページとして扱うこともできます。

例)

pages/
　└ multipage/
　　└ index.js

```jsx:/pages/multipage/index.js
import { useRouter } from "next/router";

const MultiPage = () => {
  const router = useRouter();
  const step = router.query.step ?? "0"; // null合体演算子
  const goToStep = (_step, asPath) => {
    router.push(`/multipage?step=${_step}`, asPath);
  };
  return (
    <div>
      {step === "0" && (
        <>
          <h3>Step: {step}</h3>
          <button onClick={() => goToStep(1, "/personal")}>Next Step →</button>
        </>
      )}
      {step === "1" && (
        <>
          <h3>Step: {step}</h3>
          <button onClick={() => goToStep(2, "/confirm")}>Next Step →</button>
        </>
      )}
      {step === "2" && (
        <>
          <h3>Step: {step}</h3>
          <button onClick={() => goToStep(0, "/multipage")}>Done</button>
        </>
      )}
    </div>
  );
};

export default MultiPage;

```

上記コンポーネントで`http://localhost:3000/multipage`にアクセスすると下記のように、ボタンクリックで画面が変化し、urlも設定したダミーurl、`/multipage`→`/personal`→`/confirm`に変化していくコンポーネントが作成されます。
![multipage.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/be979129-d8dc-a6fd-dc18-e8d6b55c6aef.gif)

`const step = router.query.step ?? "0";`でクエリパラメーターを取得し、stap定数に代入しています。
jsx部分で、その`step`定数に格納された値によって、表示を切り替えている形です。

### `rewrites()`の設定

上記で作成した`multipage`コンポーネントではダミーurlを設定してシングルコンポーネントで複数画面を作成していますが、ブラウザで直接ダミーurlを入力した場合、ダミーurlですので404ページが開かれます。

`http://localhost:3000/personal`をブラウザurlに直接入力
![スクリーンショット 2023-06-18 15.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/d6eec947-78fe-f869-d33c-1316998064d3.png)

そこで、ダミーurlが直接入力された場合でも、うまく動作させるためにリライトという仕組みを用います。
`next.config.js`にリライト設定を追加します。

```diff_javascript:react-guide-material/17_nextjs_p1/start/next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
+  async rewrites() {
+    return [
+     {
+        source: "/personal",
+        destination: "/multipage?step=1",
+      },
+      {
+        source: "/confirm",
+        destination: "/multipage?step=2",
+      },
+    ];
+  },
};

module.exports = nextConfig;
```

`source`のvalueに設定されたパスの入力があった場合に、`destination`のvalueに設定されたurlに紐づくコンポーネントが実行されます📌
ローカルサーバーを一度閉じてから再度立ち上げて、`http://localhost:3000/personal`をブラウザurlに直接入力してみると今度はちゃんと表示されました！

## head内にタグを挿入する方法

Next.jsで標準に提供されているコンポーネントでHeadコンポーネントというものがあります。
Headコンポーネントとはその名の通り、headタグ内に何らかのタグを挿入する際に使用します。

例)

```jsx:/pages/index.js
import Head from "next/head"; // Headコンポーネントのimport

export default function Home() {
  return (
    <>
      <Head>
        <title>テスト</title>
      </Head>
      <h1>Home</h1>
    </>
  );
}

```
上記コンポーネントを作成し、`http://localhost:3000/`にアクセスすると、headタグ内に設定した`title`が挿入されています。
このように`Head`コンポーネントを用いれば、そのコンポーネント固有のheadタグを挿入することができます。
![スクリーンショット 2023-06-18 16.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/7e4a7ef2-403a-a19c-6df0-760c1541e0e2.png)

## まとめ

今回はNext.jsの入門的な内容をまとめました✏️
次回はNextの強力な機能となるSGやSSRについてまとめたいと思います！🫡