ReactユーザーがNext.jsを勉強する

# はじめに
next.jsをお勉強することになったので、アウトプットもかねて書きます。
間違い等あったらご指摘いただけますと幸いです。

## 書くこと
- [next.jsってなんやねん](#nextjsってなんやねん)
  - [公式ドキュメント](#公式ドキュメント)
  - [特徴](#特徴)
  - [next.jsを用いることの利点](#nextjsを用いることの利点)
- [next.jsを書く上で抑えておいた方がよさそうなこと](#nextjsを書く上で抑えておいた方がよさそうなこと)
- [ディレクトリ構成どうする](#ディレクトリ構成どうする)
- [書き方のルール](#書き方のルール)
  - [特別なファイル名](#特別なファイル名)
  - [特殊なルーティングについて](#特殊なルーティングについて) 

## 各種環境
**npm**
```
10.5.0
```

**node**
```
v21.7.2
```

**react**
```
^18.3.1
```
**next**
```
^14.2.3
```
# next.jsってなんやねん

### ・公式ドキュメント
> <h4>What is Next.js?</h4>
> Next.js is a React framework for building full-stack web applications. You use React Components to build user interfaces, and Next.js for additional features and optimizations.
>
>Under the hood, Next.js also abstracts and automatically configures tooling needed for React, like bundling, compiling, and more. This allows you to focus on building your application instead of spending time with configuration.
>
>Whether you're an individual developer or part of a larger team, Next.js can help you build interactive, dynamic, and fast React applications.

引用: https://nextjs.org/docs

(__ガバ翻訳__)
next.jsはフルスタックwebアプリケーション作成のための、Reactフレームワークです。Reactを使ってインターフェースを作成し、Next.jsを使って機能の追加や最適化ができます。

内部的には、Reactに必要なバンドル、コンパイルなどのツールを自動で抽象化して設定します。これにより、余計な設定に気を取られることなく、アプリケーションの作成に集中することができます。

個人開発でも、チームの大規模開発でも、Next.jsはインタラクティブ(双方向的)で、動的で、高速なReactアプリ開発を助けてくれるでしょう。

:::note warn
**フレームワーク or ライブラリ ???**

reactはライブラリで、next.jsはフレームワーク。違いはなんだろう?

**・ライブラリ**
ライブラリは使用者に対して開発に便利な関数をまとめて提供してくれるもの。細かいパーツごとに使用できるため、自由度が高い。

**・フレームワーク**
フレームワークは、実装に必要な機能なんかがある程度まとまっていて、決められた枠組みに沿って開発できるもの。自由度は低め。

=> 
ライブラリは**ライブラリ使用者**が「どう使うか」という支配権を持つのに対し、
フレームワークは**フレームワーク側**が「どう使わせるか」という支配権を持つ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/994c6b6d-bc25-51de-1587-7735c79bf8df.png)
引用: https://ssaits.jp/promapedia/technology/library-framework.html
:::

### ・特徴
#### ・SSG(Static Site Generation)、SSR（Server-Side Rendering）、CSR（Client-Side Rendering）を統合的に扱える!

>**SSG (Static Site Generation)**
>ビルド時に、静的なページを作っておいて、リクエストを受けたらその静的なファイルを渡す手法。
>
> メリット
>・クライアント側でjsのコンパイルなどがないため、軽いのと、完全な状態のhtmlを渡すので、SEO的に対策がしやすい。
>
> デメリット
・更新作業をするときは、毎回ビルドしなきゃいけない。
___
>**SSR（Server-Side Rendering）**
クライアントからリクエストを受けた時、サーバー側でjsを実行して、htmlを生成し、それを渡す手法。
>
>メリット
・サーバーで実行するため、クライアントのマシンスペックに縛られない表示速度を保てる上、初回レンダリングも早い。
>
>デメリット
・サーバースペックが求められる。
・毎回更新時にサーバーとの通信が入る。
___

>**CSR（Client-Side Rendering）**
>クライアントからリクエストを受けた時、クライアント側でjsを実行して、htmlを生成し、domに配置、表示させる方法。reactなどはデフォでこれ。
>
>メリット
>・動的な変更に強い?
>
>デメリット
・クライアント側でjsを実行するのでマシンスペックが必要だったり、初回レンダリングが遅くなったりする。
・htmlが最低限の記述の状態でクライアント側に渡されるので、クローラーが何のサイトか判別できず、SEO対策が超貧弱になる。
___
要するに、
1. SSGはもはや公開前にビルドしておいて静的なページを作っておき、それを配信すること。
2. SSRは公開前に静的なページは生成しないけど、公開後にサーバー側でレンダリングして、その結果をHTMLで渡すから、完全なhtmlとかSEO対策のタグとかを織り込むことができて大変良い。
3. CSRは、渡すときはJSの形で渡しておいて、クライアントサイドでコンパイル、DOMに配置するから、SEO対策がし辛く、かつ初回読み込みがコンパイルのせいで遅くなりがち、ということ。


next.jsはこの3つに加え、ISR(SSGの定期更新版)の4つの公開方法を、ページごとに設定することができるっぽい。

すなわち、サイトの説明だけを載せるようなページでは、<font color='red'>頻繁に更新は要らないから、**SSG**</font>で設定してあげて、ユーザーの情報を取得するようなページでは、<font color='red'>頻繁に更新したいから、**ISRとかSSR**</font>とかを使いたいってコト？


### ・next.jsを用いることの利点

https://qiita.com/Yuki_Oshima/items/5c0dfd8f7af8fb76af8f

上記記事がとても参考になりました。
特に、

>**・ファイルベースルーティング機能**
>=> 特別にルーティングを設定しなくても、
>```typescript
>/**Page Router
>  📁page
>   L name.tsx
>=> localhost:3000/name
>*/
>
>/**App Router
>  📁test-page
>   L page.tsx //page.tsxという名前は固定
>=> localhost:3000/test-page
>*/
>```
>これでルーティングが設定できるから大変楽。

>**・開発サーバの部分的な高速リロード(Fast Refresh)**
=> 更新時に、関係のないstateは保持したまま、他の要素を更新できる。


あたりがめちゃくちゃ便利だなと感じています。

# next.jsを書く上で抑えておいた方がよさそうなこと

### ・App Router vs Page Router
基本的に、App Routerが推奨のようです。

| App Router | Page Router |
| - | - |
| appディレクトリ配下の、**ディレクトリ名**がページ名になる。<br>例) app/hogehoge/page.tsx => localhost:3000/hogehoge | pagesディレクトリ配下の、**ファイル名**がページ名になる。<br>例) pages |
| 全てのページが、デフォルトでサーバーコンポーネントになる。 | 全てのページが、デフォルトでクライアントコンポーネントになる。 

:::note warn 
・**サーバーコンポーネントと、クライアントコンポーネントの違い**

・App Routerで作成したページは、デフォルトでサーバーコンポーネントになります。サーバーコンポーネントとは、"**レンダリングがサーバーサイドで行われるコンポーネント**"のことであり、クライアントコンポーネントはその逆です。

もしApp Routerでクライアントでレンダリングさせたい場合は一行目に"use client"と書けばよいようです。

<details><summary>そもそもなぜ二種類のコンポーネントがあるのか</summary>

これにはRSC（React Server Components）という技術が深くかかわってきます。
RSCとは、<font color="red">**コンポーネントを、クライアントサイドでレンダリングするものと、サーバーサイドでレンダリングするものの二つに分けて、効率的にレンダリング処理を行う技術です。**</font>
例えばデータ取得のfetchなどは、よりDBに近いサーバーサイドで行った方が、リクエスト数も減ったりするメリットがあります。
そもそもAPIへのアクセスとかDBへのアクセスは、サーバーサイドで行った方がセキュリティ的にも安全でしょう。

こちらに関しては、以下の記事がわかりやすかったので是非ご覧ください。

https://qiita.com/newbee1939/items/7ce919f9a1a7153582b8

RSCとSSGを組み合わせることで、より効率化できそうですね。
</details>
:::

### ・サーバーコンポーネント vs クライアントコンポーネント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/a603062b-1579-4ef8-62fa-ed01b1a72e9d.png)

引用: https://levtech.jp/media/article/column/detail_399/

サーバーコンポーネントではuseEffectやuseStateは使えないということですね。

# ディレクトリ構成どうする

**コロケーションしない vs コロケーションする**

コロケーション重視でディレクトリ切った方が後々管理が楽そうな予感。

https://qiita.com/miumi/items/359b8a77bbb6f9666950

```
// コロケーションしない(引用)
├── app          ... ルーティングに関するコンポーネント
|   ├── blog
│   ├── [uuid]
│   │   └── page.tsx
│   │       └── edit
│   │           └── page.tsx
│   ├── create
│   │   └── page.tsx
│   └── page.tsx
├── login
│   └── page.tsx
├── profile
│   └── page.tsx
|
├── features     ... ロジック + コンポーネントをまとめたもの
|   ├── common   ... 共通部分
|   |   |
|   │   ├── editors
|   │   │   ├── components
|   │   │   │   ├── CodeEditor.tsx
|   │   │   │   └── RichTextEditor.tsx
|   │   │   ├── hooks.ts
|   │   │   └── stores.ts
|   │   ├── forms
|   │   │   ├── components
|   │   │   │   ├── Input.tsx
|   │   │   │   └── Select.tsx
|   │   │   ├── hooks.ts
|   │   │   └── stores.ts
|   │   └── tags
|   │       ├── components
|   │       │   ├── TagItem.tsx
|   │       │   └── TagList.tsx
|   │       ├── hooks.ts
|   │       └── stores.ts
|   |
|   └── routes   ... 特定のページで使うもの
|       └── auth
|           ├── components
|           │   └── Login.tsx
|           ├── endpoint.ts
|           └── hooks.ts
|
├── components   ... ロジックがない共通コンポーンネント
├── hooks        ... 共通ロジックの内、React Hooksが「ある」もの
├── utils        ... 共通ロジックの内、React Hooksが「ない」もの
└── constants    ... 定数を定義したファイル
```

```
//コロケーションする(想像)
├── app
│   ├── layout.ts            ... 共通レイアウト
│   ├── page.ts              ... トップページ
│   ├── about                ... Aboutページ
│   │   ├── layout.ts
│   │   ├── page.ts
│   │   ├── component.ts     ... Aboutページ特有のコンポーネント
│   │   ├── hooks.ts         ... Aboutページ特有のReact Hooks
│   │   └── styles.module.css ... Aboutページ特有のスタイル
│   └── blog                 ... Blogページ
│       ├── layout.ts
│       ├── page.ts
│       ├── post.ts          ... Blogページ特有のコンポーネント
│       ├── hooks.ts         ... Blogページ特有のReact Hooks
│       └── styles.module.css ... Blogページ特有のスタイル
├── components               ... 共通コンポーネント
│   ├── Header.ts
│   └── Footer.ts
├── hooks                    ... 共通のReact Hooks
│   └── useAuth.ts
├── utils                    ... 共通のユーティリティ
│   ├── api.ts
│   └── helpers.ts
├── constants                ... 定数を定義したファイル
│   └── config.ts
└── styles                   ... グローバルスタイル
    └── globals.css
```

- **コロケーションしない**
・メリット
　- 同種ファイルがまとまっているので、一元管理が可能。
・デメリット
　- プロジェクトが大きくなってくると、目的の機能を探すことが困難になってくる。
　- ディレクトリが大きく分かれているので、あっちこっち行くことになる。
<br/>

- **コロケーションする**
・メリット
　- 特定のページに関する機能が近くにあるので、機能を探しやすい。
　- 関係性の高いファイルが近くにあるので、リファクタや変更がしやすい。
　- ファイルの行き来が減る。
・デメリット
　- プロジェクトが大きくなってくると、そのディレクトリがめちゃくちゃ複雑になる

# 書き方のルール

### ・特別なファイル名

next.jsには特別なファイル名が用意されています。それが以下の画像です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/acdfbb09-7f40-78f1-4a33-bf8d89288c8c.png)

引用: https://nextjs.org/docs/app/building-your-application/routing

| 名前 | 意味・用法 |
| - | - |
| layout | 同じルートのページに対して、共通で表示させられるUI。<br />例) 共通のヘッダーとかフッターを記載  |
| page | app routerを使うときの、エントリーポイントになるところ。これがないとルーティングが認識されない。<br />例) app/login/page.tsx | 
| loading | 処理に時間がかかるコンポーネントを表示するときに、非同期で呼び出し、ローディング画面を表示することができる。Suspenceを使うと個別にコンポーネントに対して非同期呼び出しができるようになる。<br />例) ```<Suspence fallback={<Loading />}> ロード終了 </Suspence> ```|
| not-found | 404ページ。404エラーが発生したとき、このページが表示される。 |
| error | エラーページを出したいときに使う。上位階層のlayout.tsx や template.tsxでおきたエラーは補足できない。|
| global-error | より上位階層のエラーを回収するためにあるらしい? |
| route | api作成に用いる。.com/test-apiにアクセスすることで、APIとして使える。 |
| template | 基本はlayoutと同じだが、こちらは遷移時にstateの更新が行われる。<br /> 例) form要素をページ遷移で初期化したいとき。 |
| default | 何らかの原因でページが読み込まれなかったときに表示される内容のようです。 |


基本的に以下の階層で差し込まれるようです。
```tsx
<Layout>
  <Template>
    <ErrprBoundry fallback={<Error />}>
      <Suspense fallback={<Loading />}>
        <Page />
      </Suspense>
    </ErrprBoundry>
  </Template>
</Layout>
```

:::note warn
<details><summary>defaultの挙動の検証</summary>

例えば、以下のようなディレクトリを用意します。
```bash
parallelTest
    ├── @testA
    │   └── setting
    │       └── page.tsx
    ├── @testB
    │   ├── default.tsx
    │   └── page.tsx
    ├── default.tsx
    ├── layout.tsx
    ├── page.tsx
    └── styles.module.css
```

各ファイルの中身(一部)
```parallelTest/@testA/setting/page.tsx
"use client"

import styles from '../../styles.module.css';

export default function testA() {
  return (
    <div className={styles.testA}>this is testA!!!</div>
  )
}
```

```parallelTest/@testB/page.tsx
"use client"

import styles from '../styles.module.css';

export default function testB() {
  return (
    <div className={styles.testB}>this is testB!!!</div>
  )
}
```

```parallelTest/@testB/default.tsx
"use client"

import styles from '../styles.module.css';

export default function Default() {
  return (
    <div className={styles.testB}>Default</div>
  )
}
```

```parallelTest/default.tsx
"use client"

import styles from './styles.module.css';

export default function Default() {
  return (
    <div className={styles.testB}>Parent Default</div>
  )
}
```

この状態でhttp://localhost:3000/parallelTest/setting にアクセスすると、以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/0c7b731a-a4b2-4ab3-d64d-81bfec57cb22.png)

これは、@testAディレクトリにはsettingディレクトリがありますが、@testBディレクトリには存在せず、読み込むことができなかったため、代わりにdefault.tsxの内容が表示されているようです。

ところで、parallelTestに配置したdefault.tsxも表示されています(「Parent Default」のやつ)。
これはなぜでしょうか。

もう一度構造を見てみましょう。
```bash
parallelTest
    ├── @testA
    │   └── setting
    │       └── page.tsx
    ├── @testB
    │   ├── default.tsx
    │   └── page.tsx
    ├── default.tsx
    ├── layout.tsx
    ├── page.tsx
    └── styles.module.css
```
考えなくてはいけないのは、**settingディレクトリの有無**です。
よく見るとparallelTest直下にはsettingディレクトリがありません。

@testAディレクトリは<font color="red">**ルーティングに影響を及ぼさない**</font>だけで、settingディレクトリではないので、settingディレクトリを見つけられなかったparallelTestディレクトリはdefault.tsxを返しました。

ちゃんと配置してあげれば、default.tsxの内容は表示されません。
```bash
parallelTest
    ├── @testA
    │   ├── default.tsx
    │   └── setting
    │       └── page.tsx
    ├── @testB
    │   ├── default.tsx
    │   └── page.tsx
    ├── default.tsx
    ├── layout.tsx
    ├── page.tsx
    ├── setting // これ
    │   └── page.tsx //これ
    └── styles.module.css
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/a54b29c5-649e-1e28-dfb8-7dc9306cf89f.png)

</details>
:::

### ・特殊なルーティングについて

##### ・プライベートフォルダ
フォルダ名の最初にアンダースコア(_)をつけることでそれ以下のフォルダではページと認識されないようです。
```bash
├── app
│   ├── login
│   │   ├── _component //こいつはルーティングに含まれない
|   |   |   ├── page.tsx // 表示されない
│   │   │   └── withAuth.tsx
│   │   ├── error.tsx
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── template.tsx
```

##### ・ルートグループ
フォルダ名を" () "でくくることで、URLパスに影響を及ぼさず、ルートの整理が行えます。例えば、以下のように管理者側と、一般ユーザー側を明示的にグルーピングできたりします。

```
├── app
│   ├── homepage
│   │   ├── (admin)
│   │   │   ├── edit
│   │   │   │   └── page.tsx // localhost:3000/homepage/edit
│   │   │   └── regist
│   │   │       └── page.tsx
│   │   └── (client)
│   │       ├── blog
│   │       │   └── page.tsx // localhost:3000/homepage/blog
│   │       └── contact
│   │           └── page.tsx
```

##### ・ダイナミックルーティング
フォルダ名を" [] "でくくることで、動的なルーティングが実装できます。
例えば、```app/homepage/blog/[id]/page.tsx```のように書き、以下を記載します。
```tsx
export default function Page({ params }: { params: { id: string } }) {
  return <div>My Post: {params.id}</div>
}
//http://localhost:3000/homepage/blog/1 => My Post: 1
//http://localhost:3000/homepage/blog/2 => My Post: 2
```
アクセスするURLによってidの値は変わり、動的なルーティング設定ができます。

もし
```
http://localhost:3000/homepage/blog/study/1
```
みたいに、複数階層にわたってパラメータを取得したい場合は以下のように書き換えます。

```app/homepage/blog/[...id]/page.tsx```
```tsx
export default function Page({ params }: { params: { id: string[] } }) {
  return <div>My Post: {params.id.join(',')}</div>
}
//=> My Post: study,2
```
or
```app/homepage/blog/[category]/[id]/page.tsx```
```tsx
export default function Page({ params }: { params: { category: string, id: string } }) {
  return <div>My Post: {`${params.category}:${params.id}`}</div>
}
//=> My Post: study,2
```


しかし、このままだと、
```
http://localhost:3000/homepage/blog
```
のように、階層がない場合、404エラーになってしまうため、以下のように書き換えます。
```app/homepage/blog/[[...id]]/page.tsx```
```tsx
export default function Page({ params }: { params: { id: string[] } }) {
  return (
    (params.id) ? <div>My Post: {params.id.join(',')}</div> : <div>no id</div>
  )
}
```

##### ・平行ルーティング
フォルダ名の先頭に@をつけることで、複数のコンポーネントを並列に描画することができます。

例えば、以下のような構成で、ファイルを作成します。
```bash
parallelTest
    ├── @testA
    │   └── page.tsx
    ├── @testB
    │   ├─  loading.tsx
    |   └── page.tsx
    |   
    ├── layout.tsx
    ├── page.tsx
    └── styles.module.css
```
```loading.tsx
"use client"

export default function Loading() {
  return <div>loading...</div>
};
```

```testA/page.tsx
"use client"

import styles from '../styles.module.css';

export default function testA() {
  return (
    <div className={styles.testA}>this is testA!!!</div>
  )
}
```

```testB/page.tsx
"use client"

import { Suspense } from 'react';
import styles from '../styles.module.css';
import Loading from './loading';

export default async function testB() {
  const Await = () => {
    return new Promise(callback => { setTimeout(callback, 3000) })
  };
  
  await Await();
  return (
    <Suspense fallback={<Loading />}>
      <div className={styles.testB}>this is testB!!!</div>
    </Suspense>
  )
}
```

```layout.tsx
"use client"

import styles from './styles.module.css';

export default function Layout({
  children,
  testA,
  testB,
}: {
  children: React.ReactNode
  testB: React.ReactNode
  testA: React.ReactNode
}) {

  return (
    <>
      {children}
      <div className={styles.testContainer}>
        {testA}
        {testB}
      </div>
    </>
  )
}
```
![parallel (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/8e93cd3f-bc97-65f0-2177-1d6e5fa20f58.gif)

非同期処理でも、同時並列的に表示できていることがわかると思います。

##### ・インターセプトルート
フォルダ名の先頭に(.)をつけることで、対象のパスを乗っ取ることができるようです。

https://azukiazusa.dev/blog/nextjs-interception-routes/

このサイトが非常に参考になりました。

先頭のパスは、
(.) => カレントディレクトリ
(..) => 親ディレクトリ
(..)(..) => 二つ上のディレクトリ
(...) => appディレクトリからの相対パス
になるそうです。

簡単に説明すると、インターセプトルートを使うことで、指定したパスの表示を奪うことができます。

公式サンプル: https://github.com/vercel/nextgram 

このサンプルは以下の構造になっています。
```
app
├── @modal
│   ├── (..)photos
│   │   └── [id]
│   │       └── page.js
│   └── default.js
├── default.js
├── global.css
├── layout.js
├── opengraph-image.png
├── page.js
└── photos
    └── [id]
        └── page.js
```
@modal/(..)photos で、app/photosのパスを乗っ取っているので、平行ルーティングも併せて、modalと普通の写真ページが同時に表示されています。

うまく使うの難しそう...

# まとめ
**・next.jsの強みは、ページごとにレンダリング方法を指定できるので、より効率的でSEO対策に強いサイトを作ることができること。加えてルーティングの設定が容易であること。**

この原則を崩さないように思想を叩き込んでコーディングしていきたいなと思います。
長くなっちゃうので具体的なSSGとかSSRの書き方とかは別記事で...

ここまで読んでくださってありがとうございました!