# 02. Backend Architecture 감사

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

## 활성 backend 진입점

FastAPI 진입점은 `backend/app/main.py`다.

확인된 startup 흐름은 다음과 같다.

| 단계 | 위치 | 설명 |
|---|---|---|
| logging/i18n 초기화 | `main.py:29` 이후 | 앱 시작 시 기본 설정 |
| insecure default 검사 | `main.py` + `core/config.py` | production에서 기본 secret/password/localhost DB 차단 |
| DB 초기화 | `main.py:43`, `core/database.py` | `create_all()` + raw SQL migration |
| stale progress recovery | `main.py` | 이전 실행 중단된 progress 복구 |
| default user bootstrap | `main.py`, `startup/default_users.py` | dev/test만 default admin/creator 생성 |
| router mount | `main.py:80` 이후 | auth/users/projects/episodes/entities/images/steps 등 |

## Router map

| Router | 주요 책임 |
|---|---|
| `auth.py` | login/logout/me, cookie session |
| `users.py` | admin user management |
| `projects.py` | project CRUD, members, planning doc, LLM config |
| `episodes.py` | episode CRUD, upload, analysis/reanalysis helper, progress |
| `entities.py` | entity list/update, still/style/outlook/t2i translation |
| `images.py` | image asset list/file/review/regenerate/upload/direct generation/status |
| `exports.py`, `import_api.py` | project export/import |
| `operations.py` | operation logs |
| `prompts.py` | DB prompt template admin API |
| `steps.py` | official StepRunner single/run-all/snapshot/result API |

## 정상 의도 실행 경로

```mermaid
sequenceDiagram
    participant FE as Frontend
    participant API as /api/v1/.../steps
    participant Dispatch as analysis_dispatch_service / step_execution_service
    participant Runner as StepRunner
    participant Step as Step class
    participant CP as checkpoint manifest.json
    participant SR as step_run
    participant Sync as checkpoint_sync
    participant DB as projection tables

    FE->>API: POST run-all or single step
    API->>Dispatch: dispatch request
    Dispatch->>Runner: create runner
    Runner->>SR: read previous status/hash
    Runner->>CP: load checkpoint if resume
    Runner->>Step: execute
    Step-->>Runner: data/assets
    Runner->>CP: atomic save manifest
    Runner->>SR: upsert status/hash/error/counts
    Dispatch->>Sync: orchestrate_full_sync
    Sync->>DB: project checkpoint data
    API-->>FE: accepted/status
```

이 경로의 핵심 source는 다음이다.

| 파일 | 역할 |
|---|---|
| `backend/app/api/v1/steps.py` | 공식 step API |
| `backend/app/services/analysis_dispatch_service.py` | run-all category batch orchestration |
| `backend/app/services/step_execution_service.py` | single step background execution |
| `backend/app/core/step_runner.py` | gate, resume, execute, checkpoint, step_run update |
| `backend/app/services/checkpoint_sync/orchestrator.py` | checkpoint -> DB projection |

## 실제 이미지 실행 경로가 갈라지는 지점

이미지 계열은 공식 StepRunner path와 legacy/direct image API path가 동시에 살아 있다.

```mermaid
flowchart TB
    FE[Frontend] --> StepAPI[/steps/ref_image_gen or scene_image_pipeline/]
    FE --> RunAll[/steps/run-all?category=image/]
    FE --> LegacyRef[/images/generate-reference-images/]
    FE --> LegacyScene[/images/generate-images/]

    StepAPI --> StepExec[step_execution_service]
    RunAll --> Batch[analysis_dispatch_service]
    StepExec --> Runner[StepRunner]
    Batch --> Runner
    Runner --> ImageSteps[image step classes]
    ImageSteps --> CP[checkpoint]
    ImageSteps --> DB[(image_asset / step_run)]

    LegacyRef --> RefService[ReferenceImageService direct]
    LegacyScene --> SceneService[SceneImageService direct]
    RefService --> DB
    SceneService --> DB
    RefService --> Files[projects assets]
    SceneService --> Files
```

확인된 frontend 진입점은 다음이다.

| UI 위치 | 호출 |
|---|---|
| `EpisodeDetail.tsx` | `/steps/ref_image_gen`, `/steps/scene_image_pipeline` |
| `PipelineStepsPanel.tsx` | `/steps/run-all?category=...` |
| `useRunAllSteps.ts` | `/steps/run-all?category=...&mode=...` |
| `EpisodeActionBar.tsx` | `/generate-reference-images`, `/generate-images` |
| `Entities.tsx` | `/generate-reference-images?mode=resume/full` |

이 중 `/generate-reference-images`와 `/generate-images`는 `StepRunner`의 checkpoint/verify/status contract와 완전히 같은 경로가 아니다.

## Background job 구조

현재 background execution은 최소 세 갈래다.

| 경로 | 구현 | 리스크 |
|---|---|---|
| step run-all/single | `job_manager.py`, `task_registry.py`, service background thread | process-local, multi-worker/restart 취약 |
| direct image generation | `images.py` 내부 background handling | StepRunner 상태와 분리 |
| 일부 raw thread | `images.py` 일부 코드 | central job manager 우회 |

핵심 문제는 상태 저장 위치가 일관되지 않다는 점이다.

| 상태 | 저장 위치 |
|---|---|
| active task | in-memory dict |
| pipeline progress | `pipeline_progress` DB table |
| step execution | `step_run` DB table |
| actual step output | filesystem checkpoint |
| image asset metadata | `image_asset` DB table |
| legacy generation status | `images.py` aggregation endpoint |

## StepRunner 책임

`StepRunner`의 책임은 너무 많지만, 현재 구조에서는 중심 역할을 한다.

| 책임 | 설명 |
|---|---|
| dependency gate | 선행 step 상태 검사 |
| applicability | always/disabled/on_demand/if_* 조건 처리 |
| resume | checkpoint/schema/config hash를 보고 skip/reuse 판단 |
| stale/force 처리 | downstream invalidation 및 archive |
| execute | concrete step class 호출 |
| checkpoint write | manifest 저장 |
| `step_run` upsert | status, counts, error, sync fields |
| verification | entry/exit verify |
| projection trigger | dispatch service가 checkpoint sync 호출 |

## 상태 판단 중복

```mermaid
flowchart LR
    CP[checkpoint manifest] --> StepRead[step read model]
    SR[(step_run)] --> StepRead
    Episode[(episode.status)] --> Gate[pipeline_gate]
    Assets[(image_asset)] --> Gate
    Progress[(pipeline_progress)] --> UIProgress[progress API]
    Jobs[in-memory jobs] --> UIProgress
    StepRead --> FE[Frontend]
    Gate --> FE
```

중복 자체가 문제는 아니지만, 각 상태가 같은 truth를 보지 않으면 다음 문제가 생긴다.

| 문제 | 예시 |
|---|---|
| completed인데 DB projection 0건 | step exit verify 또는 registration failure가 잡지 못할 수 있음 |
| episode analyzed 오판 | projection sync가 개별 step 후에도 `Episode.status`를 넓게 설정 가능 |
| image status 과장 | scene asset row count와 실제 primary/selected/file 존재가 다를 수 있음 |
| stale 판단 불일치 | checkpoint hash, step_run hash, project config hash 기준이 다를 수 있음 |

## 주요 리스크

| Severity | 문제 | 근거 |
|---|---|---|
| P0/P1 | 이미지 생성 경로 이중화 | `/steps/*`와 `/generate-*`가 함께 활성 |
| P1 | background job process-local | daemon thread/in-memory registry |
| P1 | run-all과 single-step 동시성 경계 불완전 | 서로 다른 task key/check 사용 |
| P1 | StepRunner verify bypass 가능 | single-step resume completed short-circuit 경로 |
| P1 | `partial`이 downstream gate를 통과 | incomplete artifact 소비 가능 |
| P1 | DB/checkpoint 상태 동기화 실패 시 사용자에게 늦게 노출 | projection layer가 별도 |
| P2 | `api_endpoint`가 unexpected exception string을 client에 노출 | 내부 정보 leak 가능 |
| P2 | cookie secure가 request scheme 의존 | TLS terminating proxy에서 Secure 누락 가능 |

## 개선 방향

| 방향 | 설명 |
|---|---|
| 실행 경로 단일화 | 이미지 생성도 `/steps` 또는 하나의 orchestrator만 사용 |
| job queue 명확화 | process-local thread 대신 durable queue 또는 DB-backed lock |
| 상태 source 정리 | `step_run + checkpoint`를 실행 truth로 고정하고 legacy status는 projection으로 전환 |
| verification 강제 | 모든 asset step은 DB row/file count exit verify를 통과해야 completed |
| partial 정책 명시 | partial downstream 허용 여부를 step별 contract로 분리 |
| direct endpoint deprecate | `/generate-*`는 compatibility wrapper로 축소하거나 제거 |
