# 機能設計書 25-Metadata API

## 概要

本ドキュメントは、Next.js App RouterにおけるMetadata API機能の設計を記述する。Metadata APIは、`metadata`エクスポートや`generateMetadata`関数を通じてSEOメタデータ（title、description、OGP等）を宣言的に管理する機能である。

### 本機能の処理概要

**業務上の目的・背景**：SEO最適化はWebアプリケーションの重要な要素であり、各ページに適切なメタデータを設定する必要がある。Metadata APIは、ファイルシステムベースのルーティングと統合されたメタデータ管理を提供し、レイアウト階層に沿ったメタデータの継承・上書きを実現する。静的メタデータと動的メタデータの両方をサポートし、title templateやファイルベースのメタデータ（favicon、OGP画像等）にも対応する。

**機能の利用シーン**：ページタイトルとdescriptionの設定、OGP（Open Graph Protocol）メタデータの設定、Twitterカードの設定、canonical URL・alternateの設定、robots.txt相当の制御、favicon/アイコンの設定、Web App Manifestの設定など。

**主要な処理内容**：
1. ローダーツリーを走査してmetadataエクスポートと`generateMetadata`関数を収集
2. 親レイアウトから子ページへの階層的なメタデータ解決（マージ）
3. ファイルベースメタデータ（icon、apple-touch-icon、opengraph-image等）の収集・統合
4. title templateの適用とOpenGraph/Twitterメタデータの相互継承
5. Viewport情報の個別解決
6. 最終的なResolvedMetadataオブジェクトの生成

**関連システム・外部連携**：App Routerのレンダリングパイプライン、ローダーツリー、ビルドシステム（Webpackローダー経由のメタデータモジュール生成）と連携する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | Metadata APIはHTMLの`<head>`要素に出力されるメタデータを制御し、すべてのApp Routerページに影響する |

## 機能種別

メタデータ管理 / SEO制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| metadata (static) | Metadata | No | 静的メタデータオブジェクト | Metadata型に準拠 |
| generateMetadata | (props, parent) => Metadata | No | 動的メタデータ生成関数 | 非同期関数も可 |
| viewport (static) | Viewport | No | ビューポート設定 | Viewport型に準拠 |
| generateViewport | (props, parent) => Viewport | No | 動的ビューポート生成関数 | 非同期関数も可 |
| params | Promise<Params> | No | 動的ルートパラメータ | generateMetadataの引数 |
| searchParams | Promise<SearchParams> | No | 検索パラメータ（ページのみ） | ページコンポーネントのみ |

### 入力データソース

- 各ルートセグメントの`layout.tsx`/`page.tsx`からエクスポートされるmetadata/generateMetadata
- ファイルベースメタデータ（icon.png、opengraph-image.png、twitter-image.png、apple-icon.png、manifest.json等）
- 親メタデータ（resolvedMetadata）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| resolvedMetadata | ResolvedMetadata | 完全に解決されたメタデータオブジェクト |
| resolvedViewport | ResolvedViewport | 解決されたビューポート設定 |
| title | AbsoluteTemplateString | 解決されたページタイトル |
| openGraph | ResolvedOpenGraph | 解決されたOGPメタデータ |
| twitter | ResolvedTwitter | 解決されたTwitterカードメタデータ |
| icons | ResolvedIcons | 解決されたアイコン情報 |

### 出力先

- HTMLの`<head>`要素内の`<meta>`タグ、`<title>`タグ、`<link>`タグ

## 処理フロー

### 処理シーケンス

```
1. メタデータアイテムの収集
   └─ resolveMetadataItems()でローダーツリーを走査
2. 各レイヤーのメタデータ/ファイルメタデータを収集
   └─ collectMetadata()で各セグメントのmetadataエクスポートと静的ファイルを取得
3. メタデータのプリレンダー
   └─ prerenderMetadata()でgenerateMetadata関数を事前実行
4. メタデータの累積マージ
   └─ accumulateMetadata()でルートから順にマージ
5. 個別プロパティの解決
   └─ title、openGraph、twitter、icons等を個別に解決
6. ポスト処理
   └─ postProcessMetadata()でOpenGraph/Twitter間の相互継承、favicon処理
```

### フローチャート

```mermaid
flowchart TD
    A[resolveMetadata開始] --> B["resolveMetadataItems(tree)"]
    B --> C["ルートレイアウトのmetadata収集"]
    C --> D["子セグメントのmetadata収集（再帰）"]
    D --> E["ページのmetadata収集"]
    E --> F["prerenderMetadata() - generateMetadata事前実行"]
    F --> G["accumulateMetadata() - 累積マージ"]
    G --> H{generateMetadata?}
    H -->|Yes| I["親resolvedMetadataを渡してgenerateMetadata実行"]
    H -->|No| J["静的metadataをマージ"]
    I --> K["mergeMetadata() - プロパティ別マージ"]
    J --> K
    K --> L["mergeStaticMetadata() - ファイルベースメタデータ統合"]
    L --> M["titleTemplate適用"]
    M --> N["postProcessMetadata() - OG/Twitter相互継承"]
    N --> O[ResolvedMetadata返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | 下位優先 | 子セグメントのメタデータは親セグメントのメタデータを上書きする | 同一キーが定義された場合 |
| BR-25-02 | title template | 親レイアウトのtitle.templateが子のtitleに適用される | テンプレートが定義されている場合 |
| BR-25-03 | OG/Twitter自動継承 | OpenGraphの情報はTwitterメタデータに自動継承される | Twitterメタデータが未定義の場合 |
| BR-25-04 | favicon特別処理 | ルートレイアウトのfavicon.icoは最初のアイコンとして挿入 | i<=1の静的メタデータで検出時 |
| BR-25-05 | ファイルベース優先 | ファイルベースのOGP/Twitter画像はコード定義のimagesが未設定の場合のみ適用 | source?.openGraph?.images未定義時 |
| BR-25-06 | Viewport分離 | viewport関連プロパティはmetadataではなくviewportエクスポートで定義すべき | themeColor/colorScheme/viewportがmetadataに含まれる場合は警告 |

### 計算ロジック

title template適用：`template: '%s | My Site'` + `title: 'About'` -> `'About | My Site'`

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

### 操作別データベース影響一覧

該当なし。Metadata APIはHTMLメタデータの生成機能であり、データベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無効なmetadataBase | metadataBaseが有効なURLでない | 有効なURL文字列を設定 |
| - | 非推奨プロパティ警告 | apple-touch-fullscreen等をother内で使用 | appleWebApp/icons.appleを使用 |
| - | viewport設定警告 | themeColor/colorScheme/viewportをmetadataで設定 | viewportエクスポートに移動 |

### リトライ仕様

generateMetadata関数の実行が失敗した場合、フレームワークレベルでのリトライは行わない。エラーはApp Routerのエラーバウンダリで処理される。

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

該当なし。

## パフォーマンス要件

- メタデータ解決はReactのcache()関数でメモ化される
- generateMetadata関数は事前実行（プリレンダー）され、親メタデータの解決を待機する最適化が行われる
- `use cache`対応のgenerateMetadata関数はlazy実行される

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

- metadataBase URLの検証により、不正なURL注入を防止
- ユーザー入力をメタデータに含める場合は、アプリケーション層でのサニタイズが必要

## 備考

- `metadata`と`generateMetadata`は同一セグメントで共存不可
- `viewport`と`generateViewport`は同一セグメントで共存不可
- メタデータはServer Componentsでのみ利用可能（`server-only`インポート）

---

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

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

### 推奨読解順序

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

メタデータの型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | metadata-interface.ts | `packages/next/src/lib/metadata/types/metadata-interface.ts` | `Metadata`と`ResolvedMetadata`インターフェースの定義 |
| 1-2 | opengraph-types.ts | `packages/next/src/lib/metadata/types/opengraph-types.ts` | OpenGraph関連の型定義 |
| 1-3 | twitter-types.ts | `packages/next/src/lib/metadata/types/twitter-types.ts` | Twitter関連の型定義 |

**読解のコツ**: `Metadata`はユーザーが定義する入力型、`ResolvedMetadata`はフレームワークが解決した最終出力型。URLフィールドは`ResolvedMetadata`では文字列化される（`WithStringifiedURLs<T>`）。

#### Step 2: メタデータ解決のエントリーポイントを理解する

`resolveMetadata`と`resolveViewport`がメインAPI。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | resolve-metadata.ts | `packages/next/src/lib/metadata/resolve-metadata.ts` | `resolveMetadata()`関数（1257-1279行目）がエントリーポイント |

**主要処理フロー**:
1. **1266-1272行目**: `resolveMetadataItems()`でローダーツリーからメタデータアイテムを収集
2. **1273-1278行目**: `accumulateMetadata()`で累積マージ

#### Step 3: メタデータ収集ロジックを理解する

ツリー走査によるメタデータの収集。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | resolve-metadata.ts | `packages/next/src/lib/metadata/resolve-metadata.ts` | `resolveMetadataItemsImpl()`（731-798行目）の再帰走査ロジック |

**主要処理フロー**:
- **743行目**: ツリー分割（segment、parallelRoutes、page）
- **749-759行目**: 動的パラメータの処理
- **764-774行目**: `collectMetadata()`で各セグメントのメタデータを収集
- **776-789行目**: 並列ルートへの再帰

#### Step 4: マージロジックを理解する

メタデータの累積マージ処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | resolve-metadata.ts | `packages/next/src/lib/metadata/resolve-metadata.ts` | `accumulateMetadata()`（1106-1218行目）と`mergeMetadata()`（213-446行目） |

**主要処理フロー**:
- **1133行目**: `prerenderMetadata()`でgenerateMetadata関数を事前実行
- **1136-1184行目**: メタデータアイテムのループでマージ実行
- **1212-1217行目**: `postProcessMetadata()`でOG/Twitter継承とfavicon処理

#### Step 5: プロパティ別解決を理解する

個別のメタデータプロパティの解決ロジック。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | resolve-title.ts | `packages/next/src/lib/metadata/resolvers/resolve-title.ts` | titleとtitle templateの解決 |
| 5-2 | resolve-opengraph.ts | `packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts` | OpenGraphメタデータの解決 |
| 5-3 | resolve-icons.ts | `packages/next/src/lib/metadata/resolvers/resolve-icons.ts` | アイコン情報の解決 |

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

```
resolveMetadata() [resolve-metadata.ts:1257]
    |
    +-- resolveMetadataItems() [resolve-metadata.ts:707]
    |       +-- resolveMetadataItemsImpl() [resolve-metadata.ts:731]
    |           +-- collectMetadata() [resolve-metadata.ts:608]
    |           |       +-- getLayoutOrPageModule()
    |           |       +-- getDefinedMetadata()
    |           |       +-- resolveStaticMetadata()
    |           +-- 並列ルートへの再帰
    |
    +-- accumulateMetadata() [resolve-metadata.ts:1106]
            +-- prerenderMetadata() [resolve-metadata.ts:1005]
            +-- mergeMetadata() [resolve-metadata.ts:213]
            |       +-- resolveTitle()
            |       +-- resolveOpenGraph()
            |       +-- resolveTwitter()
            |       +-- resolveIcons()
            |       +-- mergeStaticMetadata()
            +-- postProcessMetadata() [resolve-metadata.ts:930]
                    +-- inheritFromMetadata()
```

### データフロー図

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

layout.tsx metadata        ──> collectMetadata()               ──> MetadataItems[]
page.tsx generateMetadata  ──> collectMetadata()               ──> MetadataItems[]
icon.png / og-image.png    ──> resolveStaticMetadata()         ──> StaticMetadata

MetadataItems[]            ──> accumulateMetadata()            ──> ResolvedMetadata
                                +-- mergeMetadata() per layer
                                +-- postProcessMetadata()

ResolvedMetadata           ──> HTMLレンダリング                ──> <head>メタタグ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| resolve-metadata.ts | `packages/next/src/lib/metadata/resolve-metadata.ts` | ソース | メタデータ解決のメインロジック（1308行） |
| metadata-interface.ts | `packages/next/src/lib/metadata/types/metadata-interface.ts` | ソース | Metadata/ResolvedMetadata型定義 |
| metadata-types.ts | `packages/next/src/lib/metadata/types/metadata-types.ts` | ソース | メタデータ基本型定義 |
| opengraph-types.ts | `packages/next/src/lib/metadata/types/opengraph-types.ts` | ソース | OpenGraph型定義 |
| twitter-types.ts | `packages/next/src/lib/metadata/types/twitter-types.ts` | ソース | Twitter型定義 |
| resolve-title.ts | `packages/next/src/lib/metadata/resolvers/resolve-title.ts` | ソース | title解決ロジック |
| resolve-opengraph.ts | `packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts` | ソース | OpenGraph解決ロジック |
| resolve-icons.ts | `packages/next/src/lib/metadata/resolvers/resolve-icons.ts` | ソース | アイコン解決ロジック |
| resolve-basics.ts | `packages/next/src/lib/metadata/resolvers/resolve-basics.ts` | ソース | 基本メタデータ解決（robots、themeColor等） |
| resolve-url.ts | `packages/next/src/lib/metadata/resolvers/resolve-url.ts` | ソース | URL解決ロジック |
| default-metadata.ts | `packages/next/src/lib/metadata/default-metadata.ts` | ソース | デフォルトメタデータ生成 |
| constants.ts | `packages/next/src/lib/metadata/constants.ts` | ソース | メタデータ関連定数 |
| is-metadata-route.ts | `packages/next/src/lib/metadata/is-metadata-route.ts` | ソース | メタデータルート判定 |
| get-metadata-route.ts | `packages/next/src/lib/metadata/get-metadata-route.ts` | ソース | メタデータルート取得 |
| generate/utils.ts | `packages/next/src/lib/metadata/generate/utils.ts` | ソース | メタデータ生成ユーティリティ |
