# 機能設計書 102-next/og（ImageResponse）

## 概要

本ドキュメントは、Next.jsのOpen Graph画像動的生成API（`next/og`の`ImageResponse`クラス）の設計を記述する。JSXからOGP画像をサーバーサイドで生成する機能である。

### 本機能の処理概要

`ImageResponse`クラスは、JSXとCSSからOpen Graph画像やTwitterカード画像をサーバーサイドで動的に生成するAPIである。`@vercel/og`ライブラリをラップし、Edge RuntimeとNode.jsランタイムの両方で動作する。

**業務上の目的・背景**：ソーシャルメディアでのリンク共有時に表示されるOGP画像を動的に生成する需要が高い。従来は事前に画像を用意する必要があったが、本機能によりリクエスト時にJSXテンプレートからPNG画像を生成でき、コンテンツに応じたカスタム画像を自動的に提供できるようになった。

**機能の利用シーン**：App RouterのRoute Handlers（`route.ts`）内やMetadata APIの`opengraph-image.tsx`ファイル内で使用される。ブログ記事のタイトル、ユーザープロフィール、動的なサムネイルなど、コンテンツに合わせたOGP画像の生成に利用される。

**主要な処理内容**：
1. ランタイム環境（Edge/Node.js）に応じた`@vercel/og`モジュールの動的インポート
2. JSX要素と設定オプションを受け取り、`@vercel/og`の`ImageResponse`でPNG画像を生成
3. ReadableStreamを通じたストリーミング画像レスポンスの構築
4. 適切なHTTPヘッダー（Content-Type、Cache-Control）の設定
5. カスタムヘッダーのマージ処理

**関連システム・外部連携**：`@vercel/og`ライブラリ（Satori + Resvgベースの画像生成エンジン）に依存する。

**権限による制御**：特に権限制御はない。Route Handlersのアクセス制御に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | OGP画像表示 | 結果表示 | ソーシャルメディアでのリンクプレビュー時に生成画像が表示される |

## 機能種別

画像生成 / API応答

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| element | ReactElement | Yes | 画像として描画するJSX要素 | @vercel/ogがサポートするJSXサブセット |
| options | ImageResponseOptions | No | 画像生成オプション（width, height, fonts, headers, status, statusText等） | - |

### 入力データソース

- Route Handlerまたはopengraph-image.tsx内でユーザーが定義するJSX要素
- リクエストパラメータ（動的画像生成のためのデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| body | ReadableStream | PNG形式の画像バイナリストリーム |
| Content-Type | string | `image/png` |
| Cache-Control | string | 開発時：`no-cache, no-store`、本番時：`public, max-age=0, must-revalidate` |
| status | number | HTTPステータスコード（デフォルト200） |
| statusText | string | HTTPステータステキスト |

### 出力先

HTTPレスポンスとしてクライアントに返却

## 処理フロー

### 処理シーケンス

```
1. ImageResponseコンストラクタが呼び出される
   └─ JSX要素とオプションが渡される
2. ReadableStreamを作成
   └─ start()コールバック内で非同期処理を開始
3. ランタイム判定とモジュールインポート
   └─ Edge Runtime: index.edge.js、Node.js: index.node.js
4. @vercel/ogのImageResponseインスタンスを生成
   └─ JSX要素がSatoriでSVGに変換され、ResvgでPNGに変換される
5. レスポンスボディのストリーミング
   └─ reader.read()ループで画像データをcontrollerにenqueue
6. HTTPヘッダーの構築
   └─ デフォルトヘッダー + カスタムヘッダーのマージ
7. Responseオブジェクトとしてsuper()呼び出し
   └─ ReadableStream、ヘッダー、ステータス情報を渡す
```

### フローチャート

```mermaid
flowchart TD
    A[new ImageResponse] --> B[ReadableStream作成]
    B --> C{NEXT_RUNTIME}
    C -->|edge| D[import index.edge.js]
    C -->|node| E[import index.node.js]
    D --> F[OGImageResponse生成]
    E --> F
    F --> G{body存在?}
    G -->|No| H[controller.close]
    G -->|Yes| I[reader.read ループ]
    I --> J{done?}
    J -->|No| K[controller.enqueue]
    K --> I
    J -->|Yes| L[controller.close]
    H --> M[ヘッダー構築]
    L --> M
    M --> N[super Response 呼び出し]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-102-1 | デフォルトContent-Type | 常に`image/png`をContent-Typeに設定 | すべてのレスポンス |
| BR-102-2 | 開発時キャッシュ無効 | 開発環境ではキャッシュを無効化（`no-cache, no-store`） | `NODE_ENV === 'development'` |
| BR-102-3 | 本番キャッシュ設定 | 本番環境では再検証必須のキャッシュ設定 | `NODE_ENV !== 'development'` |
| BR-102-4 | カスタムヘッダー優先 | ユーザー指定のヘッダーがデフォルトヘッダーを上書き | options.headersが指定された場合 |

### 計算ロジック

特になし。画像生成ロジックは`@vercel/og`ライブラリに委譲される。

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

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

該当なし。本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | モジュールインポートエラー | @vercel/ogのインポートに失敗した場合 | 非同期import失敗時にReadableStreamがエラー状態になる |
| - | 画像生成エラー | 不正なJSXまたはサポートされないCSS使用時 | @vercel/og内部でエラーがスローされる |

### リトライ仕様

リトライは不要。画像生成に失敗した場合はエラーレスポンスを返す。

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

該当なし。

## パフォーマンス要件

- 画像生成はSatori（JSX to SVG）+ Resvg（SVG to PNG）パイプラインで処理され、ReadableStreamによるストリーミング配信で応答開始を早める
- 本番環境では`public, max-age=0, must-revalidate`のCache-Control設定によりCDNキャッシュの活用が可能

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

- ユーザー入力をJSXに含める場合、XSSのリスクは低い（画像生成のため）が、サーバーリソース消費への注意が必要
- 外部フォントやリモート画像の読み込みにはセキュリティ上の検討が必要

## 備考

- `ImageResponse`は`Response`クラスを継承しており、Web標準のResponse APIと互換性がある
- `displayName`に`'ImageResponse'`が設定されており、デバッグ時の識別が容易

---

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

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

### 推奨読解順序

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

ImageResponseが受け取るパラメータとResponseの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | image-response.ts | `packages/next/src/server/og/image-response.ts` | ImageResponseクラスの型定義（ConstructorParametersで@vercel/ogの型を参照） |

**読解のコツ**: `OgModule`型エイリアスで`@vercel/og`モジュールの型を参照している。`ConstructorParameters<OgModule['ImageResponse']>`でコンストラクタの引数型を間接的に取得している。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | image-response.ts | `packages/next/src/server/og/image-response.ts` | ImageResponseコンストラクタ（19-63行目） |

**主要処理フロー**:
1. **22-43行目**: ReadableStreamの作成。start()内で非同期にモジュールインポートと画像生成を実行
2. **24-28行目**: ランタイム環境に応じたモジュールの動的インポートとImageResponse生成
3. **30-31行目**: レスポンスボディがない場合の早期クローズ
4. **34-41行目**: reader.read()ループによるデータ転送
5. **45-57行目**: HTTPヘッダーの構築（デフォルト + カスタム）
6. **58-62行目**: super()呼び出しによるResponseオブジェクトの初期化

#### Step 3: モジュールインポートの仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | image-response.ts | `packages/next/src/server/og/image-response.ts` | importModule関数（3-11行目） |

**主要処理フロー**:
- **8行目**: Edge Runtimeの場合は`index.edge.js`をインポート
- **9行目**: Node.jsの場合は`index.node.js`をインポート

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

```
Route Handler / opengraph-image.tsx
    │
    └─ new ImageResponse(element, options)
           │
           ├─ new ReadableStream({ start() })
           │      ├─ importModule()
           │      │      └─ import('@vercel/og') [Edge or Node版]
           │      ├─ new OGImageResponse(element, options)
           │      │      ├─ Satori (JSX -> SVG)
           │      │      └─ Resvg (SVG -> PNG)
           │      └─ reader.read() ループ
           │
           ├─ new Headers(defaults)
           │      └─ newHeaders.forEach(merge)
           │
           └─ super(readable, { headers, status, statusText })
```

### データフロー図

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

JSX Element ──────────> importModule() ──────────────> PNG Binary Stream
                              │
Options ──────────────> OGImageResponse
  ├─ width/height              │
  ├─ fonts                     ├─ Satori (JSX→SVG)
  ├─ headers                   ├─ Resvg (SVG→PNG)
  ├─ status                    │
  └─ statusText          ReadableStream ──────────> Response
                              │                        ├─ Content-Type: image/png
                              └─ reader.read()         ├─ Cache-Control
                                                       └─ status/statusText
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| image-response.ts | `packages/next/src/server/og/image-response.ts` | ソース | ImageResponseクラスの実装 |
| @vercel/og (compiled) | `packages/next/src/compiled/@vercel/og/` | 依存ライブラリ | JSXからPNG画像を生成するエンジン |
| navigation.ts | `packages/next/src/api/navigation.ts` | ソース | next/ogのAPIエクスポート定義 |
