# TheRoad Scene Lab — 통합 코드 리뷰 결과 & 액션 플랜

> 4개 리뷰어 (Codex GPT-5.5, Claude 백엔드, Claude 파이프라인, Claude 프론트엔드) 결과 통합
> 기준: docs/product-spec.md + docs/technical-spec.md vs 실제 코드

---

## 리뷰 요약

| 리뷰어 | 범위 | CRITICAL | IMPORTANT | MINOR |
|--------|------|----------|-----------|-------|
| Claude 백엔드 | API + Services | 4 | 6 | 6 |
| Claude 프론트엔드 | Pages + Components | 3 | 7 | 1 |
| Claude 파이프라인 | Pipeline + LLM | 5 | 7 | 1 |
| Codex | 전체 소스 | 2 | 6 | 4 |

---

## CRITICAL — 즉시 수정 필요

### C1. `entities.py:534` — `asyncio.get_event_loop().run_until_complete()` Python 3.12에서 RuntimeError

```
파일: backend/app/api/v1/entities.py:534
증상: PATCH /style-rules 호출 시 RuntimeError (이미 실행 중인 이벤트 루프)
수정: async 핸들러로 변경하거나 Pydantic body 사용
```

### C2. `image_service.py:1097` — `current_location_ids` UnboundLocalError

```
파일: backend/app/services/image_service.py:1097
증상: dependent_scene_id가 있는 씬에서 UnboundLocalError
수정: current_location_ids = [] 초기화 추가 (line 1044 이후)
```

### C3. `images.py:379,458` + `image_service.py:551` — 키 풀 가드 미적용

```
파일: backend/app/api/v1/images.py:379,458 / image_service.py:551,1869,1988,2281,2507
증상: GEMINI_API_KEY 없이 키 풀만 사용 시 불필요한 400 에러
수정: if not settings.gemini_api_key and gemini_key_count() == 0: 으로 통일
```

### C4. `EpisodeDetail.tsx:1188-1617` — 430줄 데드코드 + 미사용 상태변수 8개

```
파일: frontend/src/pages/EpisodeDetail.tsx:1188-1617
증상: {false && (...)} 블록 430줄, 관련 핸들러/상태 미사용
수정: 데드코드 블록 + 미사용 상태변수/핸들러 전체 삭제
```

### C5. `episodes.py:137-138` — 분석 백그라운드 스레드 에러 무시 (pass)

```
파일: backend/app/api/v1/episodes.py:137-138
증상: except Exception: pass → 상태가 "analyzing"에 영구 정체 가능
수정: 최소한 logging.error + 에피소드 상태 안전 복구
```

### C6. `ref_image_pipeline.py:291` + `scene_image_pipeline.py:125` — sanitize() 호출 시그니처 오류 → TypeError

```
파일: backend/app/modules/pipeline/ref_image_pipeline.py:291, scene_image_pipeline.py:125
증상: sanitize(current_prompt) — block_reason, block_categories 누락 → TypeError
      → 모더레이션 재시도 경로가 항상 실패하여 조용히 무시됨
수정: sanitize(current_prompt, exc.block_reason, exc.block_categories, attempt=...) 로 수정
```

### C7. `scene_image_pipeline.py:230-235` — angle/angle+color I2I가 edit_color()만 호출 (데드 브랜치)

```
파일: backend/app/modules/pipeline/scene_image_pipeline.py:230-235
증상: angle/angle+color 분기에서도 edit_color() 호출 → 각도 편집이 절대 실행 안 됨
수정: edit_angle() / edit_combined() 분기 구현 또는 스키마에 angle params 추가
```

### C8. `analysis_service.py:129-135` — entity upsert에 entity_type 필터 누락 → 이름 충돌

```
파일: backend/app/services/analysis_service.py:129-135
증상: character "제복"과 outlook "제복"이 같은 이름이면 데이터 덮어씀
수정: .filter(EntityCanon.entity_type == entity_type) 추가
```

### C9. `image_service.py:597` — dependent_scene_id가 씬 이미지 생성에 전달 안 됨 (Codex 발견)

```
파일: backend/app/services/image_service.py:597, 1047
증상: 분석에서 추출한 시각적 연관 씬 ID가 이미지 생성 시 무시됨
      → 연속 장면 시각 일관성 깨짐
수정: still payload 구성 시 dependent_scene_id 포함
```

### C10. `image_service.py:2137` — 커스텀 이미지 업로드 시 프로젝트 소유권 검증 누락 (Codex 발견)

```
파일: backend/app/services/image_service.py:2137-2167
증상: entity_id/still_id가 다른 프로젝트 소유여도 이미지 연결 가능
수정: entity/still의 project_id == self._project_id 검증 추가
```

---

## IMPORTANT — 다음 스프린트에 수정

### I1. LLMCallLog 커버리지 부족

```
영향 모듈:
  - ref_image_pipeline.py — GPT Vision 검증 (직접 urllib)
  - variation_recommender_v2.py — GPT Vision 추천 (직접 urllib)
  - pdf_validator.py — GPT Vision PDF 검증 (직접 urllib)
  - entity_extractor_v2.py — Turn 1.5 GPT 리뷰 (직접 urllib)
수정: 이 모듈들의 GPT 호출을 OpenAIClient로 교체하거나 llm_logger 직접 호출 추가
```

### I2. 스타일 규칙 전용 페이지 미구현

```
기획서: product-spec.md 3.6절 "스타일 규칙 편집"
현황: EpisodeDetail.tsx 안에 접힌 패널로만 존재
수정: /projects/:id/style-rules 전용 페이지 + 사이드바 링크 추가
```

### I3. 프론트엔드 에피소드 분석 에러 무시

```
파일: frontend/src/pages/Episodes.tsx:133
증상: handleAnalyze catch {} → 사용자에게 실패 피드백 없음
수정: 에러 토스트/알림 표시
```

### I4. MemberList — UUID로 멤버 초대 (UX 불가)

```
파일: frontend/src/components/shared/MemberList.tsx:138
증상: user_id UUID 직접 입력 필요 → 실사용 불가
수정: username 기반 검색 + 자동완성으로 변경
```

### I5. EpisodeDetail 탭 비활성

```
파일: frontend/src/pages/EpisodeDetail.tsx:1105-1109
증상: 씬/엔티티 탭 전환 불가, 단일 탭 하드코딩
수정: 탭 onClick 복원 + entities 탭 렌더링
```

### I6. 인라인 프롬프트/스키마 외부화 미완료

```
영향 모듈:
  - entity_extractor_v2.py Turn 1.7: system_prompt 인라인
  - outlook_merger.py: system_prompt 인라인
  - world_guide_generator.py: WORLD_GUIDE_SCHEMA 하드코딩
  - pdf_validator.py: VALIDATION_SCHEMA 하드코딩
  - prompt_sanitizer.py: SANITIZE_SCHEMA 하드코딩
수정: 프롬프트 파일로 점진적 이전
```

### I7. Entities.tsx — N+1 순차 API 호출

```
파일: frontend/src/pages/Entities.tsx:172-188
증상: 에피소드당 순차 API 호출 → 프로젝트 규모 커지면 병목
수정: Promise.all() 병렬 호출 또는 프로젝트 레벨 집계 API
```

### I8. ProjectDetail 개요 탭 — 에피소드 현황 누락

```
파일: frontend/src/pages/ProjectDetail.tsx
기획서: product-spec.md 3.2절 "에피소드 현황"
수정: 에피소드 수/상태 요약 위젯 추가
```

### I9. ImageReview 상태 필터 — 클라이언트 사이드 + regenerating 누락

```
파일: frontend/src/pages/ImageReview.tsx:75-91
수정: API에 status 파라미터 전달 + regenerating 상태 추가
```

### I10. api/client.ts — FormData 업로드 시 Content-Type 충돌

```
파일: frontend/src/api/client.ts:4
증상: Content-Type: application/json 하드코딩 → FormData 업로드 시 문제
수정: FormData일 때 Content-Type 자동 생략
```

### I11. `reanalyze_scenes` — 아웃룩/씬 연관 재추출 누락

```
파일: backend/app/services/analysis_service.py:931-938
증상: 씬 재분석 시 outlook_assignments, scene_dependencies 없이 호출
      → 재분석 씬의 T2I 프롬프트 품질 저하 (아웃룩 마커 누락)
수정: reanalyze_scenes에도 extract_scene_dependencies + extract_all_outlooks 추가
```

### I12. `outlook_extractor.py:91` — 모델명 하드코딩

```
파일: backend/app/modules/pipeline/outlook_extractor.py:91
증상: "gemini-3.1-pro-preview" 문자열 직접 → .env 변경 시 미반영
수정: settings.gemini_text_model 사용
```

### I13. `world_guide_generator.py:9` — 프롬프트 경로 하드코딩

```
파일: backend/app/modules/world_guide_generator.py:9
증상: "prototype_prompts/v5" 하드코딩 → 버전 관리 컨벤션 위반
수정: _get_latest_version_dir() 패턴 적용
```

### I14. `image_service.py:651-673` — WorldGuide 중복 생성

```
파일: backend/app/services/image_service.py:651-673
증상: generate_images() 호출마다 WorldGuide 신규 생성 (기존 체크 없음)
수정: generate_reference_images_only() 패턴처럼 기존 체크 후 재사용
```

---

## MINOR — 기회 있을 때 정리

### M1. 접근성 — 대시보드/에피소드 카드 키보드 접근 불가
### M2. version_registry.py — 모듈 버전이 코드 변경과 동기화 안 됨
### M3. dead imports — analysis_service.py 상단 미사용 임포트 (v1 모듈)
### M4. 순차 모드 (v3) 코드 — scene_extractor_v2.py 540~660줄 데드코드 (항상 병렬 모드)
### M5. `projects.py:206-207` — _get_project() 이중 호출 (결과 미사용)
### M6. `image_service.py:129` — 하드코딩 모델명 "gemini-3-flash-preview" in tracer log
### M7. `image_service.py:1214` — `if False:` I2I 데드코드 ~100줄
### M8. `config.py:15-17` — 기본 비밀번호 하드코딩 (admin123/creator123)
### M9. `analysis_service.py:624,892` — reanalysis 시 SceneStill 삭제하지만 연관 ImageAsset 미삭제 → 고아 이미지 (Codex)
### M10. `episode_service.py:97` — PDF 업로드 실패 시 디스크에 쓰인 파일 미삭제 (Codex)
### M11. `ActivityLog.tsx:35` — 프론트 from/to 파라미터 vs 백엔드 date_from/date_to 불일치 (Codex)
### M12. `ExportStudio.tsx:152` — 웹북 없이 PDF 렌더링 가능 (기획서 순서 위반) (Codex)
### M13. `Entities.tsx:470` — "전체" 탭에서 outlook 타입 미표시 (기획서 위반) (Codex)
### M14. `gemini_key_pool.py:60` — 키 번호 불연속 시 이후 키 무시 (GEMINI_API_KEY1 없고 GEMINI_API_KEY2만 있으면 무시) (Codex)

---

## 액션 플랜 — 우선순위별 실행 계획

```mermaid
gantt
    title 수정 우선순위 타임라인
    dateFormat YYYY-MM-DD
    section P0 Critical (8건)
    C1 asyncio 에러             :crit, c1, 2026-03-20, 1d
    C2 UnboundLocalError        :crit, c2, 2026-03-20, 1d
    C3 키풀 가드 통일           :crit, c3, 2026-03-20, 1d
    C4 프론트 데드코드 삭제      :crit, c4, 2026-03-20, 1d
    C5 백그라운드 에러 핸들링    :crit, c5, 2026-03-20, 1d
    C6 sanitize() 시그니처 수정  :crit, c6, 2026-03-20, 1d
    C7 I2I angle 데드브랜치     :crit, c7, 2026-03-20, 1d
    C8 entity upsert 타입필터   :crit, c8, 2026-03-20, 1d
    section P1 Important (14건)
    I1 LLMCallLog 커버리지      :i1, 2026-03-21, 2d
    I3 프론트 에러 피드백       :i3, 2026-03-21, 1d
    I5 탭 복원                 :i5, 2026-03-21, 1d
    I10 FormData 수정          :i10, 2026-03-21, 1d
    I11 reanalyze 아웃룩 추가  :i11, 2026-03-21, 1d
    I12 모델명 하드코딩 제거   :i12, 2026-03-22, 1d
    I13 프롬프트 경로 수정     :i13, 2026-03-22, 1d
    I14 WorldGuide 중복 방지   :i14, 2026-03-22, 1d
    I6 프롬프트 외부화         :i6, 2026-03-22, 2d
    I2 스타일 규칙 페이지      :i2, 2026-03-23, 1d
    I4 멤버 초대 UX            :i4, 2026-03-23, 1d
    I7 N+1 API 최적화         :i7, 2026-03-23, 1d
    I8 에피소드 현황 위젯      :i8, 2026-03-24, 1d
    I9 상태 필터 개선          :i9, 2026-03-24, 1d
    section P2 Minor (8건)
    M1~M8 코드 정리           :m1, 2026-03-25, 2d
```

### Phase 0: 즉시 수정 (3/20) — 8건 Critical
C1~C8 버그 수정 → 커밋 → Codex 리뷰

### Phase 1a: 파이프라인 안정화 (3/21) — 5건
- I1 LLMCallLog 커버리지 (직접 urllib → OpenAIClient 교체)
- I11 reanalyze_scenes에 아웃룩/의존성 추출 추가
- I3 프론트 에러 피드백
- I5 탭 복원
- I10 FormData 수정

### Phase 1b: 코드 정합성 (3/22) — 4건
- I12 모델명 하드코딩 → settings 참조
- I13 프롬프트 경로 → 버전 관리 컨벤션
- I14 WorldGuide 중복 생성 방지
- I6 인라인 프롬프트 외부화

### Phase 1c: UI/UX 보완 (3/23~24) — 5건
- I2 스타일 규칙 페이지
- I4 멤버 초대 UX
- I7 N+1 API 최적화
- I8 에피소드 현황 위젯
- I9 상태 필터 개선

### Phase 2: 정리 (3/25~) — 8건 Minor
- M1~M8 데드코드, 접근성, 버전 동기화, 하드코딩 비밀번호
