# 機能設計書 55-redirect / permanentRedirect

## 概要

本ドキュメントは、Next.jsの`redirect`および`permanentRedirect`関数の機能設計を記述する。これらの関数はServer Components、Route Handlers、Server Actionsからサーバーサイドリダイレクトを実行するためのAPIであり、特殊なエラーオブジェクト（RedirectError）をスローする制御フローベースの仕組みを採用している。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションでは、認証状態に応じたリダイレクト、フォーム送信後の結果ページへの遷移、URLの正規化（旧URLから新URLへの転送）など、サーバーサイドでのリダイレクトが必要な場面が頻繁に発生する。`redirect`/`permanentRedirect`は、Server Components内でも宣言的にリダイレクトを実行できるAPIを提供し、従来のgetServerSidePropsでのリダイレクトパターンを拡張する。

**機能の利用シーン**：未認証ユーザーのログインページへのリダイレクト、フォーム送信後の完了ページへの遷移、レガシーURLの新URLへの恒久リダイレクト（301/308）、条件に応じたページ転送。

**主要な処理内容**：
1. RedirectErrorオブジェクトの生成（URL、タイプ、ステータスコードをdigestに格納）
2. エラーのスロー（never型の戻り値 - 必ず例外をスロー）
3. Server Actions内ではデフォルトでpushタイプ、それ以外はreplaceタイプ
4. redirect: 307（一時的リダイレクト）/ 303（Server Actionsから）
5. permanentRedirect: 308（恒久リダイレクト）/ 303（Server Actionsから）
6. Next.jsのエラーハンドリング機構がRedirectErrorをキャッチしてHTTPリダイレクトレスポンスに変換

**関連システム・外部連携**：Action Async Storage（Server Actions判定）、App Renderのエラーハンドリング機構と連携。

**権限による制御**：関数自体に権限制御はないが、認証・認可チェック後のリダイレクト手段として広く使用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | サーバーサイド処理のため特定の画面に紐づかない |

## 機能種別

ナビゲーション / サーバーサイド制御フロー

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url (redirect) | string | Yes | リダイレクト先URL | - |
| type (redirect) | RedirectType ('push' \| 'replace') | No | リダイレクトタイプ（デフォルト: Server Actionsでは'push'、それ以外は'replace'） | - |
| url (permanentRedirect) | string | Yes | リダイレクト先URL | - |
| type (permanentRedirect) | RedirectType | No | リダイレクトタイプ（デフォルト: 'replace'） | - |

### 入力データソース

- 関数引数としてのURL文字列
- actionAsyncStorage（Server Actions判定用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| RedirectError | never (Error) | NEXT_REDIRECT コードとdigest（リダイレクト情報）を持つエラーオブジェクト |

### 出力先

- エラーとしてスローされ、Next.jsのエラーハンドリング機構でキャッチされる
- Server Componentではmeta tagリダイレクトに変換
- Route Handler / Server Actionでは307/308/303 HTTPレスポンスに変換

## 処理フロー

### 処理シーケンス

```
1. redirect(url, type?) 呼び出し
   ├─ type未指定の場合: actionAsyncStorageからServer Actions判定
   │   ├─ isAction === true: type = 'push'
   │   └─ それ以外: type = 'replace'
   └─ getRedirectError(url, type, TemporaryRedirect) でエラー生成
2. permanentRedirect(url, type?) 呼び出し
   └─ getRedirectError(url, type, PermanentRedirect) でエラー生成
3. getRedirectError(url, type, statusCode) 実行
   ├─ new Error(REDIRECT_ERROR_CODE) でエラーオブジェクト生成
   └─ error.digest = `NEXT_REDIRECT;${type};${url};${statusCode};` 設定
4. throw error（never型 - 関数からは戻らない）
5. Next.js エラーハンドリング機構
   ├─ isRedirectError() でリダイレクトエラーを識別
   ├─ getURLFromRedirectError() でURLを抽出
   ├─ getRedirectTypeFromError() でタイプを抽出
   └─ getRedirectStatusCodeFromError() でステータスコードを抽出
```

### フローチャート

```mermaid
flowchart TD
    A[redirect/permanentRedirect 呼び出し] --> B{redirect or permanent?}
    B -->|redirect| C{type 指定?}
    B -->|permanentRedirect| D[type = replace]
    C -->|Yes| E[指定 type を使用]
    C -->|No| F{Server Action 内?}
    F -->|Yes| G[type = push]
    F -->|No| H[type = replace]
    E --> I[getRedirectError]
    G --> I
    H --> I
    D --> I
    I --> J[Error + digest 生成]
    J --> K[throw error]
    K --> L[Next.js エラーハンドリング]
    L --> M{実行コンテキスト}
    M -->|Server Component| N[meta tag リダイレクト]
    M -->|Route Handler| O[307/308 HTTP レスポンス]
    M -->|Server Action| P[303 HTTP レスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-55-01 | Server Actionsデフォルトpush | Server Actions内でtype未指定の場合、pushタイプとなる | actionAsyncStorage.isAction === true |
| BR-55-02 | 一時リダイレクトステータス | redirect()は307 TemporaryRedirectを使用 | 常時 |
| BR-55-03 | 恒久リダイレクトステータス | permanentRedirect()は308 PermanentRedirectを使用 | 常時 |
| BR-55-04 | digestフォーマット | `NEXT_REDIRECT;{type};{url};{statusCode};` 形式で情報を格納 | 常時 |
| BR-55-05 | never型戻り値 | 両関数は必ず例外をスローし、呼び出し元には戻らない | 常時 |

### 計算ロジック

- digest文字列: セミコロン区切りで`REDIRECT_ERROR_CODE`, `type`, `url`, `statusCode`を格納
- URL抽出: digestをセミコロンでsplitし、index 2からlast-2までのスライスをjoin（URLにセミコロンが含まれる場合を考慮）

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NEXT_REDIRECT | RedirectError | redirect()/permanentRedirect()呼び出し時 | Next.jsのエラーハンドリングが自動キャッチ |
| - | Error | isRedirectErrorでないエラーに対してgetRedirectTypeFromError呼び出し | 'Not a redirect error'をスロー |

### リトライ仕様

該当なし（リダイレクトはリトライ対象外）

## トランザクション仕様

該当なし

## パフォーマンス要件

- エラーオブジェクトの生成は軽量で、パフォーマンスへの影響は最小限

## セキュリティ考慮事項

- URLのバリデーションは呼び出し元の責任（redirect関数自体はバリデーションを行わない）
- オープンリダイレクト脆弱性を防ぐため、リダイレクト先URLの検証は呼び出し側で行う必要がある

## 備考

- `unstable_rethrow`関数と組み合わせることで、try-catch内でのRedirectErrorの適切な再スローが可能
- `isRedirectError`関数はdigestの構造を検証して正当なリダイレクトエラーかを判定する
- RedirectStatusCodeには`TemporaryRedirect`(307)と`PermanentRedirect`(308)が定義されている

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | redirect-error.ts | `packages/next/src/client/components/redirect-error.ts` | **3-9行目**: REDIRECT_ERROR_CODE定数、RedirectType型、RedirectError型 - digestのフォーマット定義 |
| 1-2 | redirect-status-code.ts | `packages/next/src/client/components/redirect-status-code.ts` | RedirectStatusCode enum - TemporaryRedirect(307), PermanentRedirect(308) |

**読解のコツ**: RedirectErrorはError型を拡張し、digest文字列にリダイレクト情報をエンコードするパターンを使用。Next.jsの他のエラー型（NotFoundError等）も同様のdigestパターンを採用している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | redirect.ts | `packages/next/src/client/components/redirect.ts` | **38-46行目**: `redirect`関数 - Server Actions判定とエラースロー |
| 2-2 | redirect.ts | `packages/next/src/client/components/redirect.ts` | **59-65行目**: `permanentRedirect`関数 - 恒久リダイレクト |

**主要処理フロー**:
1. **9-14行目**: actionAsyncStorageの条件付きrequire（サーバーサイドのみ）
2. **16-24行目**: `getRedirectError` - Errorオブジェクト生成とdigest設定
3. **43行目**: `type ??= actionAsyncStorage?.getStore()?.isAction ? 'push' : 'replace'` - Server Actions判定
4. **45行目**: `throw getRedirectError(url, type, RedirectStatusCode.TemporaryRedirect)`

#### Step 3: エラー判定ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | redirect-error.ts | `packages/next/src/client/components/redirect-error.ts` | **18-42行目**: `isRedirectError`関数 - digestのパース・検証 |
| 3-2 | redirect.ts | `packages/next/src/client/components/redirect.ts` | **74-97行目**: `getURLFromRedirectError`, `getRedirectTypeFromError`, `getRedirectStatusCodeFromError` |

### プログラム呼び出し階層図

```
redirect(url, type?) / permanentRedirect(url, type?)
    |
    +-- actionAsyncStorage.getStore() [server only]
    |       └─ isAction 判定
    |
    +-- getRedirectError(url, type, statusCode)
    |       +-- new Error(REDIRECT_ERROR_CODE)
    |       +-- error.digest = 格納
    |
    +-- throw error  ──────────────>  Next.js Error Handling
                                        |
                                        +-- isRedirectError(error)
                                        +-- getURLFromRedirectError(error)
                                        +-- getRedirectTypeFromError(error)
                                        +-- getRedirectStatusCodeFromError(error)
```

### データフロー図

```
[入力]                      [処理]                         [出力]

url: string -----------> getRedirectError() -----------> RedirectError
type: RedirectType                                         |
statusCode: number                                         v
                         throw error ---------> Next.js Error Handler
                                                    |
                                                    +-- Server Component: meta tag
                                                    +-- Route Handler: 307/308 response
                                                    +-- Server Action: 303 response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| redirect.ts | `packages/next/src/client/components/redirect.ts` | ソース | redirect/permanentRedirect関数（98行） |
| redirect-error.ts | `packages/next/src/client/components/redirect-error.ts` | ソース | RedirectError型とisRedirectError判定（43行） |
| redirect-status-code.ts | `packages/next/src/client/components/redirect-status-code.ts` | ソース | ステータスコードenum |
| action-async-storage.external.ts | `packages/next/src/server/app-render/action-async-storage.external.ts` | ソース | Server Actions AsyncLocalStorage |
