Next.jsのファイルシステムルーティング


# Next.jsのファイルシステムルーティング

https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages

チュートリアルを行っていた時にNext.jsのファイルシステムルーティングがわからなかったのでメモ。

## 1. URLにしたいページは`app`ディレクトリ配下に`page.tsx`を作成する


* `page.tsx`とはReactコンポーネントをエクスポートする特別なNext.jsファイル
* `/app/page.tsx`→ホームページとなる
* `app/URLにしたいディレクトリ名/page.tsx`→ディレクトリ名をパスにしたURLとなる
    * ディレクトリ名を`dashboard`にしてその中に`page.tsx`を作成
        * http://localhost:3000/dashboard


## 2. ナビゲーションなどの共通化したい部分は`layout.tsx`を使う

* `layout.tsx`とはナビゲーションなど複数のページで共有されるUIを作成するためのファイル
    * そのディレクトリ以下のすべてのページに適用される
    * 例：`app/dashboard/layout.tsx` → `/dashboard`
* ページコンポーネントのみが更新され、レイアウトは再レンダリングされないことが特徴

```typescript
// app/dashboard/layout.tsx
export default function Layout({ children }) {
  return (
    <div>
      <SideNav />
      {children}
    </div>
  );
}
```

* ルートにある`/app/layout.tsx`（ルートレイアウト）についてはフォントなどアプリケーション内のすべての<html>ページで共有したいことを書く
    * ルートレイアウトは必須
        * `<html>`と`<body>`タグを含める必要がある


## 3. URLに影響を与えたくない場合には ルートグループ（括弧付きディレクトリ）を使う

* 例：`app/(dashboard)/page.tsx` → URLは `/` になる（`(dashboard)`はURLに含まれない）
* コードの整理や共通のレイアウトをグループ化するのに使う

```
app/
├─ (dashboard)/        ← URLに含まれない
│   ├─ page.tsx       ← URL: /
│   └─ layout.tsx     ← このレイアウトは (dashboard) 内のページにのみ適用
└─ (auth)/            ← URLに含まれない
    ├─ login/
    │   └─ page.tsx   ← URL: /login
    └─ register/
        └─ page.tsx   ← URL: /register
```

## 4. 同じURLで複数のページを表示させたい場合にはパラレルルート（`@folder`）を使う

* パラレルルートは同じURLで複数のページを同時に表示する機能
* パラレルルートはlayout.tsxでのみ使える
    * page.tsxではパラレルルートを受け取れない
    * layout.tsx で受け取る際、フォルダ名と同じ名前のプロパティで受け取る

```tsx
  export default function Layout({ children, analytics, settings }) {
    return (
      <div>
        {children}      {/* メインのページ */}
        {analytics}     {/* @analytics の内容 */}
        {settings}      {/* @settings の内容 */}
      </div>
    );
  }
```

* フォルダ名を`@`で始めると、同じ階層のlayout.tsxに渡して両方表示することができる

```
app/
└─ dashboard/
    ├─ @analytics/    ← パラレルルート
    │   └─ page.tsx   ← 分析ページ
    ├─ @settings/     ← パラレルルート
    │   └─ page.tsx   ← 設定ページ
    ├─ layout.tsx     ← 両方のページを表示するレイアウト
    └─ page.tsx       ← メインのページ
```

## 5. モーダル表示したい場合にはインターセプト

* インターセプトルートは同じURLでモーダルを表示する機能
* 同じURLでモーダルを表示したい場合、`(..)`をディレクトリ名につける「インターセプト（横取り）」が使える
    * メリット
        * URLが変わらない：モーダルを開いてもURLが変わらない
        * 戻るボタンで閉じられる：ブラウザの戻るボタンでモーダルを閉じられる
* モーダルを開いた状態のURLをシェアするにはクエリパラメータを使う
    * 例：`/tasks?id=task-123`


```
app/
└─ tasks/
    ├─ page.tsx       ← タスク一覧
    └─ @modal/
        ├─ modal.tsx  ← モーダル自体
        └─ (..)tasks/
            └─ page.tsx  ← タスク詳細/編集
```

## 6. ローディングUI（`loading.tsx`）

* ページの読み込み中に表示するUIとして`loading.tsx`を作成することもできる
    * 例：`app/dashboard/loading.tsx` → ダッシュボードの読み込み中に表示
* ローディングUIは自動的に表示される
    * ページの読み込み中に自動的に表示され、読み込みが完了すると自動的に非表示になる


## 7. エラーハンドリング（`error.tsx`）

* エラー発生時に表示するUI
* 例：`app/dashboard/error.tsx` → ダッシュボードでエラーが発生したときに表示
* エラーハンドリングはクライアントコンポーネントとして実装する必要がある
    * `'use client'`ディレクティブを追加する

## 8. `Not Found`（`not-found.tsx`）

* ページが見つからないときに表示するUI
    * 例：`app/dashboard/not-found.tsx` → ダッシュボードのページが見つからないときに表示
* データが見つからない場合、ユーザーが見つからない場合など`notFound()`関数を呼び出すと`Not Found`ページを表示できる

# まとめ

ファイルシステムルーティングを使う場合にはこれらを使って作成していく

* URLにしたいページ → `app/URLにしたいディレクトリ名/page.tsx`
* 共通のレイアウト → Playout.tsx`
* URLに影響を与えたくない場合 → ルートグループ（括弧付きディレクトリ）
* 同じURLで複数のページを表示 → パラレルルート（`@folder`）
* モーダル表示 → インターセプトルート（`(..)`）

```
app/
├─ (dashboard)/        ← URLに含まれない
│   ├─ page.tsx       ← URL: /
│   └─ layout.tsx     ← このレイアウトは (dashboard) 内のページにのみ適用
├─ tasks/
│   ├─ page.tsx       ← タスク一覧
│   └─ @modal/
│       ├─ modal.tsx  ← モーダル自体
│       └─ (..)tasks/
│           └─ page.tsx  ← タスク詳細/編集
└─ layout.tsx         ← ルートレイアウト
```


# その他：サーバーサイド側のロジックってどこにおくんだろう？


## 1. appディレクトリ内に配置

* Next.jsの公式ドキュメントで使っているのは`app/lib`配下におく方法👀

```
app/
├─ lib/              ← ロジックを配置
│   ├─ actions.ts    ← サーバーアクション
│   └─ utils.ts      ← ユーティリティ関数
├─ components/       ← コンポーネント
└─ dashboard/
    └─ page.tsx
```

## 2. appディレクトリと並列に配置

```
プロジェクトルート/
├─ app/             ← ページとレイアウト
├─ lib/             ← ロジックを配置
└─ components/      ← コンポーネント
```

## 3. `'use client'と'use server'`を使って同一ファイル内で定義することも可能

* `'use client'` → ファイルの先頭に書く
* `'use server'` → 関数の先頭に書く

```tsx
'use client';  // ファイル全体がクライアントコンポーネントになる

export default function Page() {
  // クライアントコンポーネント
  const [count, setCount] = useState(0);

  // サーバーアクション
  async function handleClick() {
    'use server';  // この関数のみサーバーサイドで実行
    await db.update('users', { count: count + 1 });
  }

  return <button onClick={handleClick}>クリック</button>;
}
```