# 機能設計書 32-ライブプレビュー

## 概要

本ドキュメントは、Payload CMSのライブプレビュー機能について記述する。この機能は、管理画面でのドキュメント編集内容をフロントエンドでリアルタイムにプレビュー表示する仕組みを提供する。JavaScript SDK、React Hook、Vueコンポーネントの3形態で提供され、様々なフロントエンドフレームワークに対応する。

### 本機能の処理概要

**業務上の目的・背景**：コンテンツ管理において、編集内容が最終的にどのように表示されるかを確認することは重要なワークフローである。従来は保存後にフロントエンドを確認する必要があったが、ライブプレビュー機能により、保存前の編集中のコンテンツをリアルタイムで確認できる。これにより、コンテンツ作成者の生産性向上とミス削減を実現する。

**機能の利用シーン**：
- 管理画面でのドキュメント編集時のリアルタイムプレビュー確認
- 下書き状態のコンテンツの見た目確認
- マルチロケール対応コンテンツの言語切り替えプレビュー
- リレーションシップフィールドの変更反映確認

**主要な処理内容**：
1. 管理画面（iframe親）からプレビュー画面（iframe子）へのpostMessageによるデータ送信
2. プレビュー画面でのメッセージ受信とデータマージ処理
3. APIを経由したリレーションシップデータの補完取得
4. データ変更時のコールバック実行とUI更新

**関連システム・外部連携**：
- Payload CMS管理画面（メッセージ送信元）
- フロントエンドアプリケーション（React/Vue/その他）
- Payload REST API（データ補完用）

**権限による制御**：
- プレビュー機能自体はフロントエンド側の実装であり、Payload側の認証に依存
- API呼び出し時はcredentials: 'include'によりCookieベースの認証を継承

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | ドキュメント編集画面 | 主機能 | リアルタイムプレビュー機能（有効時） |

## 機能種別

データ同期 / リアルタイム通信 / UI更新

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| serverURL | string | Yes | PayloadサーバーのURL | 有効なURL形式 |
| initialData | T extends Record<string, any> | Yes | 初期表示データ | オブジェクト型 |
| depth | number | No | リレーションシップの深度 | 0以上の整数 |
| apiRoute | string | No | APIルートパス（デフォルト: /api） | - |
| requestHandler | CollectionPopulationRequestHandler | No | カスタムリクエストハンドラー | 関数型 |

### 入力データソース

- window.postMessageイベント（管理画面からのデータ送信）
- サーバーサイドレンダリング時の初期データ
- Payload REST API（リレーションシップデータの補完）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | T | マージされたドキュメントデータ |
| isLoading | boolean | ローディング状態フラグ |

### 出力先

- Reactコンポーネントのstate（useLivePreview）
- コールバック関数（subscribe）
- DOM更新（RefreshRouteOnSave）

## 処理フロー

### 処理シーケンス

```
1. 初期化フェーズ
   └─ subscribe関数でメッセージリスナー登録
   └─ ready関数で親ウィンドウに準備完了を通知

2. メッセージ受信フェーズ
   └─ window.addEventListener('message', onMessage)
   └─ isLivePreviewEventでイベント種別判定

3. データマージフェーズ
   └─ handleMessage呼び出し
   └─ mergeData関数でAPIを経由したデータ補完
   └─ 前回データ（previousData）との統合

4. UI更新フェーズ
   └─ コールバック関数呼び出し
   └─ React setState / Vue reactive更新
```

### フローチャート

```mermaid
flowchart TD
    A[管理画面でドキュメント編集] --> B[postMessageでデータ送信]
    B --> C[プレビュー画面でメッセージ受信]
    C --> D{isLivePreviewEvent?}
    D -->|No| E[無視]
    D -->|Yes| F[handleMessage呼び出し]
    F --> G{リレーション補完必要?}
    G -->|Yes| H[mergeData - API呼び出し]
    G -->|No| I[データ直接使用]
    H --> J[previousDataとマージ]
    I --> J
    J --> K[コールバック実行]
    K --> L[UI再レンダリング]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | オリジン検証 | serverURLと一致するオリジンからのメッセージのみ処理 | メッセージ受信時 |
| BR-02 | キャッシュ管理 | ナビゲーション時にpreviousDataをリセット | subscribe呼び出し時 |
| BR-03 | 初期データフォールバック | メッセージ未受信時は初期データを返却 | handleMessage内 |
| BR-04 | ローディング状態管理 | データ受信完了までisLoading=true | useLivePreview内 |

### 計算ロジック

リレーションシップ補完APIのエンドポイント生成：
```
endpoint = globalSlug
  ? `globals/${globalSlug}`
  : `${collectionSlug}/${initialData.id}`
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| データ補完 | 対象コレクション | SELECT | リレーションシップの実データ取得 |

### テーブル別操作詳細

※ライブプレビュー自体はフロントエンド処理。API経由でのデータ取得はPayload REST APIに委譲。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告 | RefreshRouteOnSaveでrefresh関数未提供 | console.errorで警告出力 |
| - | ネットワークエラー | mergeData API呼び出し失敗 | 前回データを維持 |

### リトライ仕様

- API呼び出し失敗時のリトライは実装されていない
- 次回の編集イベントで再試行される

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

- フロントエンド処理のためトランザクション不要
- API呼び出しは読み取り専用

## パフォーマンス要件

- postMessageは非同期処理であり、UIブロッキングなし
- 大量のリレーションシップデータがある場合、API呼び出しによる遅延の可能性

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

- postMessageのオリジン検証による不正メッセージの排除
- credentials: 'include'による認証継承
- X-Payload-HTTP-Method-Overrideヘッダーによるメソッドオーバーライド

## 備考

- React、Vueそれぞれに専用パッケージが提供される
- カスタムフレームワークはcore SDK（@payloadcms/live-preview）を直接利用可能

---

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

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

### 推奨読解順序

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

まず、メッセージイベントの型定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/live-preview/src/types.ts` | LivePreviewMessageEvent型の構造 |

**読解のコツ**: `LivePreviewMessageEvent<T>`がpostMessageで送受信されるデータの型。`collectionSlug`または`globalSlug`と`data`が主要なプロパティ。

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

subscribe関数がメッセージリスナー登録の起点。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | subscribe.ts | `packages/live-preview/src/subscribe.ts` | メッセージリスナー登録処理 |
| 2-2 | ready.ts | `packages/live-preview/src/ready.ts` | 準備完了通知処理 |

**主要処理フロー**:
1. **5-12行目**: subscribe関数の引数定義
2. **17行目**: resetCache()呼び出しで前回データクリア
3. **19-29行目**: onMessage関数でhandleMessage呼び出し
4. **32-34行目**: window.addEventListenerでリスナー登録

#### Step 3: メッセージ処理を理解する

handleMessageがデータマージのコア処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | handleMessage.ts | `packages/live-preview/src/handleMessage.ts` | メッセージ処理ロジック |
| 3-2 | isLivePreviewEvent.ts | `packages/live-preview/src/isLivePreviewEvent.ts` | イベント種別判定 |

**主要処理フロー**:
- **6-15行目**: previousDataキャッシュの定義
- **19-21行目**: resetCache関数
- **23-64行目**: handleMessage関数本体
- **33行目**: isLivePreviewEventでイベント検証
- **42-52行目**: mergeData呼び出し
- **54行目**: previousDataへのキャッシュ

#### Step 4: データマージを理解する

mergeData関数がAPIを経由したデータ補完を担当。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | mergeData.ts | `packages/live-preview/src/mergeData.ts` | データマージ処理 |

**主要処理フロー**:
- **3-20行目**: defaultRequestHandler（APIフェッチ処理）
- **22-62行目**: mergeData関数本体
- **46-59行目**: requestHandler呼び出しとJSON解析
- **55-57行目**: エンドポイントURL生成

#### Step 5: React統合を理解する

useLivePreview HookがReactアプリケーション向けの抽象化を提供。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | useLivePreview.ts | `packages/live-preview-react/src/useLivePreview.ts` | React Hook実装 |
| 5-2 | RefreshRouteOnSave.tsx | `packages/live-preview-react/src/RefreshRouteOnSave.tsx` | 保存時リフレッシュコンポーネント |

**主要処理フロー**:
- **41-62行目**: useLivePreview Hookの型定義
- **64-65行目**: useState(data, isLoading)
- **73-94行目**: useEffectでsubscribe/unsubscribe管理
- **86-88行目**: ready()呼び出し

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

```
useLivePreview (React Hook)
    │
    ├─ subscribe (live-preview)
    │      ├─ resetCache
    │      └─ window.addEventListener('message', onMessage)
    │             │
    │             └─ handleMessage
    │                    ├─ isLivePreviewEvent
    │                    └─ mergeData
    │                           └─ requestHandler (fetch API)
    │
    └─ ready (live-preview)
           └─ window.parent.postMessage
```

### データフロー図

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

管理画面                  プレビュー画面
    │                          │
    │  postMessage             │
    │  (編集データ)            │
    ├──────────────────────▶   │
    │                          │
    │                     subscribe
    │                          │
    │                     handleMessage
    │                          │
    │                     isLivePreviewEvent
    │                          │ Yes
    │                          ▼
    │                     mergeData ────▶ Payload API
    │                          │              │
    │                          │◀─────────────┘
    │                          │         (関連データ)
    │                          ▼
    │                     callback(data)
    │                          │
    │                          ▼
    │                     setState(data)
    │                          │
    │                          ▼
    │                     UIレンダリング ────▶ 更新された画面
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/live-preview/src/index.ts` | ソース | パッケージエントリーポイント |
| types.ts | `packages/live-preview/src/types.ts` | ソース | 型定義 |
| subscribe.ts | `packages/live-preview/src/subscribe.ts` | ソース | メッセージリスナー登録 |
| unsubscribe.ts | `packages/live-preview/src/unsubscribe.ts` | ソース | メッセージリスナー解除 |
| ready.ts | `packages/live-preview/src/ready.ts` | ソース | 準備完了通知 |
| handleMessage.ts | `packages/live-preview/src/handleMessage.ts` | ソース | メッセージ処理 |
| mergeData.ts | `packages/live-preview/src/mergeData.ts` | ソース | データマージ |
| isLivePreviewEvent.ts | `packages/live-preview/src/isLivePreviewEvent.ts` | ソース | イベント種別判定 |
| isDocumentEvent.ts | `packages/live-preview/src/isDocumentEvent.ts` | ソース | ドキュメントイベント判定 |
| useLivePreview.ts | `packages/live-preview-react/src/useLivePreview.ts` | ソース | React Hook |
| RefreshRouteOnSave.tsx | `packages/live-preview-react/src/RefreshRouteOnSave.tsx` | ソース | 保存時リフレッシュ |
| package.json | `packages/live-preview/package.json` | 設定 | パッケージ定義 |
| package.json | `packages/live-preview-react/package.json` | 設定 | Reactパッケージ定義 |
