# 06. Frontend 및 API Surface 감사

> 원본 코드/프롬프트/기존 문서를 수정하지 않고 생성한 read-only 감사 문서입니다.

## Frontend 구조

Frontend는 Vite/React 앱이다.

| 경로 | 역할 |
|---|---|
| `frontend/src/App.tsx` | route registration |
| `frontend/src/components/layout/AppShell.tsx` | app shell |
| `frontend/src/components/layout/Sidebar.tsx` | navigation |
| `frontend/src/api/client.ts` | shared API helper |
| `frontend/src/pages/*` | page components |
| `frontend/src/hooks/api/*` | React Query hooks/mutations |
| `frontend/src/components/shared/PipelineStepsPanel.tsx` | step UI |
| `frontend/src/components/episode/EpisodeActionBar.tsx` | episode action buttons |

## Route surface

```mermaid
flowchart TB
    App[App.tsx] --> Auth[Login / PrivateRoute / AdminRoute]
    Auth --> Dashboard[Dashboard]
    Auth --> Project[ProjectDetail]
    Auth --> Episode[EpisodeDetail / Episodes]
    Auth --> Entities[Entities]
    Auth --> Images[ImageReview]
    Auth --> Export[ExportStudio]
    Auth --> Prompts[PromptManager]
    Auth --> Admin[Admin pages]
```

## API client

`frontend/src/api/client.ts`는 다음 특성을 가진다.

| 특성 | 설명 |
|---|---|
| relative API path | `/api/...` |
| cookie auth | `credentials: "include"` |
| default timeout | 30s |
| JSON request default | FormData가 아니면 `Content-Type: application/json` |
| error normalization | `ApiError` throw |
| success response | `res.json()` 가정 |

위험:

| 위험 | 설명 |
|---|---|
| empty response 처리 취약 | 204/empty success면 `res.json()`에서 throw 가능 |
| 일부 direct fetch bypass | upload flow 일부가 shared client를 우회 |
| timeout message 혼동 | client timeout이어도 server job은 계속 돌 수 있음 |

## Frontend의 pipeline 실행 진입점

```mermaid
flowchart LR
    EpisodeDetail --> StepRef[/steps/ref_image_gen/]
    EpisodeDetail --> StepScene[/steps/scene_image_pipeline/]
    PipelinePanel --> RunAll[/steps/run-all/]
    UseRunAll --> RunAll
    EpisodeActionBar --> LegacyRef[/generate-reference-images/]
    EpisodeActionBar --> LegacyScene[/generate-images/]
    Entities --> LegacyRef
```

중요한 점은 같은 "참조 이미지/씬 이미지 생성" 기능이 UI 위치에 따라 다른 backend path를 부른다는 것이다.

| UI | Endpoint | Backend path |
|---|---|---|
| `EpisodeDetail.tsx` | `/steps/ref_image_gen` | StepRunner |
| `EpisodeDetail.tsx` | `/steps/scene_image_pipeline` | StepRunner |
| `PipelineStepsPanel.tsx` | `/steps/run-all?category=...` | StepRunner batch |
| `useRunAllSteps.ts` | `/steps/run-all?category=...&mode=...` | StepRunner batch |
| `EpisodeActionBar.tsx` | `/generate-reference-images` | legacy/direct image service |
| `EpisodeActionBar.tsx` | `/generate-images` | legacy/direct image service |
| `Entities.tsx` | `/generate-reference-images` | legacy/direct image service |

이 구조는 같은 버튼처럼 보여도 다음 결과를 만들 수 있다.

| 차이 | 영향 |
|---|---|
| checkpoint 생성 여부 | StepRunner path만 step checkpoint/status와 강하게 연결 |
| `step_run` 갱신 여부 | legacy path는 step completed로 보이지 않을 수 있음 |
| exit verify | StepRunner path와 direct path에서 보장 수준 다름 |
| progress/status | UI status polling source가 다를 수 있음 |
| concurrency lock | job key가 달라 중복 실행 가능 |

## PromptManager와 prompt source mismatch

Prompt admin UI는 `/api/v1/prompts`를 통해 DB `prompt_template`만 본다.

하지만 active prompt는 상당수가 `prompts/_base` file fallback을 통해 로드된다. 따라서 UI에서 "active prompt가 없다" 또는 "DB prompt가 inactive"처럼 보여도 실제 runtime은 file prompt를 사용할 수 있다.

```mermaid
flowchart TB
    PromptManager[PromptManager UI] --> PromptAPI[/api/v1/prompts]
    PromptAPI --> DBPrompt[(prompt_template)]
    Runtime[Runtime prompt_loader] --> DBPrompt
    Runtime --> FilePrompt[prompts/_base fallback]
    PromptManager -. does not see fallback as active .-> FilePrompt
```

## Image review/status API risk

`images.py`의 generation status는 여러 table과 file existence를 aggregate한다.

주의할 부분:

| 항목 | 리스크 |
|---|---|
| scene_done count | 모든 scene asset distinct still_id 기준이면 primary/selected/file 존재와 불일치 가능 |
| composite status | prompt_used regex format 차이에 따라 count 다를 수 있음 |
| image file route | GET 요청에서 thumbnail 생성 side effect 가능 |
| regenerate/upload | direct fetch path와 shared client path 혼재 |

## Frontend test coverage

확인된 frontend test file은 5개다.

| test | 범위 |
|---|---|
| `Dashboard` 계열 | smoke 수준 |
| `StepRunStatusBadge` | badge display |
| `useRunAllSteps` | run-all mutation URL/method |
| `useRepairProjection` | mutation |
| `useStillMutations` | still mutation |

Uncovered major flows:

| 영역 | 예 |
|---|---|
| episode upload | PDF upload, planning doc, failure cleanup |
| image generation controls | legacy vs StepRunner buttons |
| entity upload/reference generation | direct fetch handling |
| PromptManager | DB-only prompt visibility |
| ImageReview | review/regenerate/validate flows |
| ExportStudio | export/import UI |
| auth/admin | role/redirect/session expiry |

## 권장 개선 방향

| 우선순위 | 개선 |
|---|---|
| P1 | image generation UI entrypoint를 StepRunner path로 단일화 |
| P1 | legacy direct image buttons는 숨기거나 compatibility label 표시 |
| P1 | shared API client가 empty success response를 처리하도록 변경 |
| P1 | direct fetch upload 경로도 `res.ok`와 error body를 강제 확인 |
| P2 | PromptManager에 file fallback prompt visibility/provenance 표시 |
| P2 | generation status를 step_run/checkpoint 기반으로 재정렬 |
| P2 | major page mutation tests 추가 |
