# 機能設計書 15-ローディングUI

## 概要

本ドキュメントは、Next.js App RouterにおけるローディングUI機能の設計を記述する。loading.tsxによるストリーミング対応のローディング状態表示機能であり、React Suspenseと統合される。

### 本機能の処理概要

ローディングUI機能は、ルートセグメントの読み込み中に表示されるフォールバックUIを定義し、React Suspenseバウンダリとして機能する。

**業務上の目的・背景**：Webアプリケーションでは、データ取得やコンポーネント読み込みに時間がかかる場合、ユーザーにローディング状態を視覚的に示す必要がある。loading.tsxを配置するだけで、対応するルートセグメントにSuspenseバウンダリが自動的に設定され、ストリーミングレンダリングと連携したスムーズなローディング体験を提供する。

**機能の利用シーン**：データフェッチを伴うページの読み込み中表示、ナビゲーション時の画面遷移表示、Server Componentの非同期処理待ち表示で利用される。

**主要な処理内容**：
1. `loading.tsx` ファイルの検出とLoaderTreeへの組み込み
2. サーバーサイドでのLoadingコンポーネントとスタイル・スクリプトの解決
3. LoadingModuleData（RSC、スタイル、スクリプト）のパッケージング
4. クライアントサイドでのLoadingBoundaryProvider/LoadingBoundaryによるSuspenseバウンダリの構築
5. ストリーミングレンダリング時のフォールバック表示と完了後のコンテンツ差し替え

**関連システム・外部連携**：React Suspense、ストリーミングレンダリング（Fizz）。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ローディング表示 | 主画面 | ルートセグメントの読み込み中に表示されるフォールバックUI |

## 機能種別

UI表示制御 / ストリーミング制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | loading.tsxはpropsを受け取らない | - |

### 入力データソース

- ファイルシステム（`app/**/loading.tsx`）
- LoaderTreeのmodulesオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| LoadingModuleData | [ReactNode, ReactNode, ReactNode] | [ローディングRSC, スタイル, スクリプト]の3要素タプル |
| Suspense fallback | ReactNode | Suspenseバウンダリのフォールバックとして使用 |

### 出力先

- HTMLストリームの初期フォールバック部分
- クライアントサイドDOMのフォールバック表示

## 処理フロー

### 処理シーケンス

```
1. サーバーサイド: LoaderTreeからloadingモジュールの取得
   └─ parseLoaderTreeでmodules.loadingを抽出
2. サーバーサイド: Loadingコンポーネントとアセットの解決
   └─ createComponentStylesAndScriptsでCSS/JSを収集
3. サーバーサイド: LoadingModuleDataの生成
   └─ [Loading, loadingStyles, loadingScripts]のタプル構築
4. クライアントサイド: LoadingBoundaryProviderでコンテキスト提供
   └─ parentLoadingDataとしてLoadingModuleDataを設定
5. クライアントサイド: LoadingBoundaryでSuspenseバウンダリ構築
   └─ loading !== nullの場合、Suspenseコンポーネントでラップ
6. ストリーミング: フォールバック表示
   └─ 子コンポーネントの準備完了前にフォールバックHTMLを送信
7. ストリーミング: コンテンツ差し替え
   └─ 子コンポーネント準備完了後、実コンテンツをストリーム送信
```

### フローチャート

```mermaid
flowchart TD
    A[LoaderTree解析] --> B{loading.tsx存在?}
    B -->|Yes| C[Loadingコンポーネント取得]
    B -->|No| D[Suspenseなしで子を直接表示]
    C --> E[CSS/JSアセット解決]
    E --> F[LoadingModuleData生成]
    F --> G[LoadingBoundaryProvider]
    G --> H[LoadingBoundary]
    H --> I[Suspenseバウンダリ構築]
    I --> J[fallbackとしてLoading表示]
    J --> K{子コンポーネント準備完了?}
    K -->|No| L[ローディング表示継続]
    K -->|Yes| M[実コンテンツに差し替え]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Suspenseバウンダリ自動構築 | loading.tsx配置で自動的にSuspenseバウンダリが作成される | loading.tsx存在時 |
| BR-02 | ストリーミング統合 | ローディングUIはHTMLストリーミングのフォールバックとして送信される | サーバーレンダリング時 |
| BR-03 | 並列ルート対応 | 各並列ルートスロットに対して個別にSuspenseバウンダリが構築される | 並列ルート使用時 |
| BR-04 | loading未定義時 | loading.tsxが存在しない場合、Suspenseバウンダリは作成されない | loading.tsx非存在時 |

### 計算ロジック

該当なし。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | デフォルトexport不在 | loading.tsxにデフォルトexportがない | コンパイルエラー |

### リトライ仕様

該当なし。Suspenseの再試行はReactフレームワークに委譲される。

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

該当なし。

## パフォーマンス要件

- ストリーミングレンダリングにより、ローディングUIはページの初期HTMLの一部として即座に送信される
- フォールバック表示中にサーバーサイドでデータフェッチが並行実行される

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

特になし。

## 備考

- loading.tsxはSuspenseバウンダリのfallbackとして機能するため、ネストされたSuspenseバウンダリの最も近いものが適用される
- hasLoadingComponentInTree関数でツリー内のloading存在チェックが行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-router-types.ts | `packages/next/src/shared/lib/app-router-types.ts` | LoadingModuleData型の定義を理解 |

**読解のコツ**: LoadingModuleDataは `[ReactNode, ReactNode, ReactNode]` の3要素タプルで、順に[ローディングコンポーネント, スタイル, スクリプト]。nullの場合はローディングが未定義。

#### Step 2: サーバーサイドのローディング処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | create-component-tree.tsx | `packages/next/src/server/app-render/create-component-tree.tsx` | loadingモジュールの解決処理（184-192行目） |

**主要処理フロー**:
1. **139-147行目**: modules.loadingの抽出
2. **184-192行目**: createComponentStylesAndScriptsによるLoading/loadingStyles/loadingScriptsの解決

#### Step 3: クライアントサイドのSuspenseバウンダリ構築

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | layout-router.tsx | `packages/next/src/client/components/layout-router.tsx` | LoadingBoundaryProvider（402-445行目）でローディングデータの提供 |
| 3-2 | layout-router.tsx | `packages/next/src/client/components/layout-router.tsx` | LoadingBoundary（451-486行目）でSuspenseバウンダリの構築 |

**主要処理フロー**:
- **402-445行目**: LoadingBoundaryProviderがLayoutRouterContextを通じてparentLoadingDataを設定
- **451-486行目**: LoadingBoundaryがloading !== nullの場合にSuspenseコンポーネントでラップ
- **466-482行目**: SuspenseのfallbackにloadingRsc/loadingStyles/loadingScriptsを設定

#### Step 4: ローディング存在チェック

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | has-loading-component-in-tree.ts | `packages/next/src/server/app-render/has-loading-component-in-tree.ts` | ツリー内のloading.tsx存在チェック |

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

```
createComponentTreeInternal (create-component-tree.tsx)
    |
    +-- parseLoaderTree
    |      └─ modules.loading の抽出
    |
    +-- createComponentStylesAndScripts (loading用)
    |      └─ [Loading, loadingStyles, loadingScripts] 生成
    |
    +-- hasLoadingComponentInTree (存在チェック)

クライアントサイド:
OuterLayoutRouter (layout-router.tsx)
    |
    +-- LoadingBoundaryProvider
    |      └─ parentLoadingData = LoadingModuleData
    |
    +-- LoadingBoundary
           └─ <Suspense fallback={loading}>
                  {children}
              </Suspense>
```

### データフロー図

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

loading.tsx -----------> parseLoaderTree --------> LoadingModuleData
                                                        |
                         createComponentStylesAndScripts |
                                                        v
                         LoadingBoundaryProvider ----> parentLoadingData
                                                        |
                         LoadingBoundary ------------> <Suspense fallback>
                                                        |
HTMLストリーム --------- フォールバック送信 -----------> ローディングUI表示
                         コンテンツ準備完了 -----------> 実コンテンツ差し替え
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| create-component-tree.tsx | `packages/next/src/server/app-render/create-component-tree.tsx` | ソース | サーバーサイドのloading解決 |
| layout-router.tsx | `packages/next/src/client/components/layout-router.tsx` | ソース | LoadingBoundaryProvider/LoadingBoundary |
| has-loading-component-in-tree.ts | `packages/next/src/server/app-render/has-loading-component-in-tree.ts` | ソース | ツリー内loading存在チェック |
| create-component-styles-and-scripts.tsx | `packages/next/src/server/app-render/create-component-styles-and-scripts.tsx` | ソース | CSS/JSアセット解決 |
| app-router-types.ts | `packages/next/src/shared/lib/app-router-types.ts` | ソース | LoadingModuleData型定義 |
