# 機能設計書 32-カスタムDocument

## 概要

本ドキュメントは、Next.jsのPages RouterにおけるカスタムDocument（`_document.tsx`）機能の設計を記述する。カスタムDocumentはHTMLドキュメント構造（`<html>`、`<head>`、`<body>`タグ）のカスタマイズを可能にし、サーバーサイドでのみレンダリングされるテンプレートコンポーネントである。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションでは、HTMLの基本構造をカスタマイズする必要がある場面が多い。例えば、lang属性の設定、カスタムmetaタグの追加、CSS-in-JSライブラリのサーバーサイドレンダリング対応、外部フォントの読み込みなどである。カスタムDocumentはこれらのHTML文書レベルのカスタマイズを統一的に管理する機能を提供する。

**機能の利用シーン**：開発者がHTML文書のlang属性を変更する場合、CSS-in-JSライブラリ（styled-componentsなど）のサーバーサイドスタイル注入を行う場合、カスタムフォントのプリロードを設定する場合、beforeInteractiveスクリプトを挿入する場合に利用される。

**主要な処理内容**：
1. HTML文書構造（html、head、body）のサーバーサイドレンダリング
2. CSSリンクタグ、スクリプトタグの自動注入と最適化
3. `__NEXT_DATA__`スクリプトによるサーバーデータのクライアント引き渡し
4. next/fontマニフェストに基づくフォントのpreload/preconnectリンク生成
5. beforeInteractiveスクリプトの読み込み管理

**関連システム・外部連携**：Pages Routerのレンダリングパイプライン、ビルドマニフェスト、next/fontマニフェスト、next/scriptコンポーネントとの連携。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | ドキュメント (_document) | 主画面 | HTMLドキュメント構造の定義とサーバーサイドレンダリング用テンプレート生成 |

## 機能種別

テンプレート生成 / サーバーサイドレンダリング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| html | string | Yes | renderPageで生成されたHTML文字列 | - |
| head | JSX.Element[] | No | ページのHeadコンポーネントから収集された要素 | - |
| styles | ReactElement[] | No | CSS-in-JSのスタイル要素 | - |
| nonce | string | No | CSP nonceトークン | - |
| crossOrigin | string | No | crossOrigin属性値 | 'anonymous' / 'use-credentials' / '' |

### 入力データソース

- renderPage関数によるページレンダリング結果
- BuildManifestからのCSS/JSファイルリスト
- NextFontManifestからのフォント情報
- HtmlContextからの各種設定値

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HTMLドキュメント | string | 完全なHTML文書（DOCTYPE含む） |
| DocumentInitialProps | DocumentInitialProps | html文字列とhead要素、styles |

### 出力先

- HTTPレスポンスとしてクライアントに送信

## 処理フロー

### 処理シーケンス

```
1. Document.getInitialPropsの実行
   └─ renderPage()を呼び出してページをレンダリング
   └─ html文字列とhead要素を取得
2. Htmlコンポーネントのレンダリング
   └─ <html>タグにlang属性、data-dpl-id属性を設定
   └─ scriptLoaderアイテムの処理
3. Headコンポーネントのレンダリング
   └─ CSSリンクタグの生成
   └─ JSプリロードリンクの生成
   └─ フォントリンクの生成
   └─ beforeInteractiveスクリプトの挿入
4. Mainコンポーネントのレンダリング
   └─ ページコンテンツの注入ポイントを提供
5. NextScriptコンポーネントのレンダリング
   └─ __NEXT_DATA__のJSON埋め込み
   └─ ポリフィル・動的チャンク・ページスクリプトの出力
```

### フローチャート

```mermaid
flowchart TD
    A[レンダリング開始] --> B[Document.getInitialProps]
    B --> C[renderPage実行]
    C --> D[html + head取得]
    D --> E[Htmlコンポーネント]
    E --> F[Headコンポーネント]
    F --> G{optimizeCss?}
    G -->|Yes| H[CSS後方配置]
    G -->|No| I[CSS前方配置]
    H --> J[Mainコンポーネント]
    I --> J
    J --> K[NextScriptコンポーネント]
    K --> L[__NEXT_DATA__埋め込み]
    L --> M{disableOptimizedLoading?}
    M -->|Yes| N[スクリプト後方読み込み]
    M -->|No| O[スクリプト最適化読み込み]
    N --> P[HTML出力完了]
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | サーバーサイド専用 | Documentコンポーネントはサーバーサイドでのみレンダリングされる。クライアントサイドでは実行されない | 常時 |
| BR-32-02 | title警告 | Head内で`<title>`タグを使用した場合に警告を出力する | 開発モード時 |
| BR-32-03 | viewport meta警告 | Head内で`<meta name="viewport">`を使用した場合に警告を出力する | 開発モード時 |
| BR-32-04 | 大規模ページデータ警告 | `__NEXT_DATA__`のサイズが閾値を超えた場合に警告を出力する | largePageDataBytesが設定されている場合 |
| BR-32-05 | FOUC防止 | 開発モードでは`body{display:none}`スタイルを注入しFOUCを防止する | 開発モード時 |

### 計算ロジック

特になし。

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

該当なし。カスタムDocumentはデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | getInitialPropsの戻り値に循環参照が含まれる場合 | シリアライズ可能なオブジェクトを返すよう修正 |
| - | Error | next/scriptでsrcもinline scriptも指定されていない場合 | srcまたはdangerouslySetInnerHTMLまたはchildrenを指定する |

### リトライ仕様

リトライは行わない。

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

該当なし。

## パフォーマンス要件

- `disableOptimizedLoading`がfalseの場合、スクリプトはdefer属性付きでHead内に配置され、並列読み込みが最適化される
- `optimizeCss`が有効な場合、CSSリンクはHead末尾に配置される

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

- nonce属性を指定することでContent Security Policy (CSP) に対応可能
- `__NEXT_DATA__`にはサーバーサイドで取得したデータが含まれるため、機密情報の漏洩に注意
- crossOrigin属性によるCORS制御が可能

## 備考

- `NEXT_BUILTIN_DOCUMENT`シンボルにより、ユーザーがカスタムDocumentを使用しているかどうかを内部的に判別可能
- App Routerでは`_document.tsx`に相当する機能はルートレイアウト（`app/layout.tsx`）に置き換えられている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | utils.ts | `packages/next/src/shared/lib/utils.ts` | DocumentType、DocumentContext、DocumentInitialProps、DocumentPropsの型定義を確認する |

**読解のコツ**: **25-29行目**で`DocumentType`、**184-190行目**で`DocumentContext`（renderPage関数を含む）、**192-194行目**で`DocumentInitialProps`（html文字列とstyles）、**196行目**で`DocumentProps`が定義されている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | _document.tsx | `packages/next/src/pages/_document.tsx` | Document本体の実装を確認する |

**主要処理フロー**:
1. **962-984行目**: Documentクラスの定義。`getInitialProps`（969-971行目）は`ctx.defaultGetInitialProps`を呼び出す。`render`（973-983行目）はHtml、Head、body、Main、NextScriptを配置する
2. **372-733行目**: Headコンポーネント。CSSリンク、JSプリロード、フォントリンク、beforeInteractiveスクリプトのレンダリングを行う
3. **792-923行目**: NextScriptコンポーネント。`__NEXT_DATA__`のJSON埋め込み、dev用ファイル、ポリフィル、スクリプトの出力を行う
4. **925-949行目**: Html関数コンポーネント。lang属性の設定とscriptLoaderアイテムの処理を行う
5. **951-956行目**: Main関数コンポーネント。ページコンテンツの注入ポイント（`<next-js-internal-body-render-target />`）を提供する

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _document.tsx | `packages/next/src/pages/_document.tsx` | 各ヘルパー関数の役割を確認する |

**主要処理フロー**:
- **54-66行目**: `getDocumentFiles` - BuildManifestからCSS/JSファイルリストを取得
- **68-95行目**: `getPolyfillScripts` - ポリフィルスクリプトタグを生成
- **101-129行目**: `getDynamicChunks` - 動的インポートのスクリプトタグを生成
- **131-162行目**: `getScripts` - ページスクリプトとlow priorityスクリプトのタグを生成
- **311-364行目**: `getNextFontLinkTags` - フォントのpreload/preconnectリンクを生成
- **735-790行目**: `handleDocumentScriptLoaderItems` - next/scriptのbeforeInteractive処理

#### Step 4: コンポーネント読み込み層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | load-components.ts | `packages/next/src/server/load-components.ts` | Documentモジュールの読み込みロジックを確認する |

**主要処理フロー**:
- **183行目**: `requirePage('/_document', distDir, false)`でDocumentモジュールを読み込む
- **304行目**: `interopDefault(DocumentMod)`でデフォルトエクスポートを取得

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

```
PagesRouteModule.render()
    │
    ├─ renderToHTMLImpl()
    │      ├─ Document.getInitialProps(ctx)
    │      │      └─ ctx.defaultGetInitialProps(ctx)
    │      │             └─ renderPage()
    │      │
    │      └─ Document.render()
    │             ├─ Html()
    │             │      └─ handleDocumentScriptLoaderItems()
    │             ├─ Head.render()
    │             │      ├─ getCssLinks()
    │             │      ├─ getPreloadDynamicChunks()
    │             │      ├─ getPreloadMainLinks()
    │             │      ├─ getPolyfillScripts()
    │             │      ├─ getPreNextScripts()
    │             │      ├─ getDynamicChunks()
    │             │      ├─ getScripts()
    │             │      └─ getNextFontLinkTags()
    │             ├─ Main()
    │             └─ NextScript.render()
    │                    ├─ getInlineScriptSource()
    │                    ├─ getPolyfillScripts()
    │                    ├─ getPreNextScripts()
    │                    ├─ getDynamicChunks()
    │                    └─ getScripts()
    │
    └─ postProcessHTML()
```

### データフロー図

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

BuildManifest ──────▶ getDocumentFiles() ──────▶ CSS/JSファイルリスト
                                │
NextFontManifest ───▶ getNextFontLinkTags() ───▶ フォントリンクタグ
                                │
renderPage() ───────▶ Document.getInitialProps() ▶ html + head + styles
                                │
HtmlContext ────────▶ Head/NextScript.render() ──▶ 完全なHTMLドキュメント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| _document.tsx | `packages/next/src/pages/_document.tsx` | ソース | デフォルトDocumentコンポーネントの実装 |
| utils.ts | `packages/next/src/shared/lib/utils.ts` | ソース | Document関連型定義 |
| html-context.shared-runtime.ts | `packages/next/src/shared/lib/html-context.shared-runtime.ts` | ソース | HtmlContextの定義（レンダリング設定値の受け渡し） |
| load-components.ts | `packages/next/src/server/load-components.ts` | ソース | _documentモジュールの読み込み |
| render.tsx | `packages/next/src/server/render.tsx` | ソース | ページレンダリング処理 |
| get-page-files.ts | `packages/next/src/server/get-page-files.ts` | ソース | ビルドマニフェストからのファイル取得 |
