이미지 생성 파이프라인 캔버스 UI

TheRoad Scene Lab · 설계서 (design) · 2026-06-29 · 작성: Claude · 리뷰: Codex(요청) / 사용자(승인 대기)
경로: docs/w21b-pipeline-canvas-ui-20260629/design.html
한 줄 요약. 프로젝트의 이미지 생성 파이프라인 전체를 에피소드 단위 노드-링크 캔버스로 시각화한다. 모든 노드는 이미지(FP·인물/소품 레퍼런스·배경 플레이트·스케치/가이드·합성·씬 스틸)이며, 엣지는 "이 이미지를 생성할 때 실제 입력으로 쓰인 이미지" 관계다. 노드를 클릭하면 그 이미지 생성에 쓰인 상류(upstream) 연결고리 노드만 밝게, 나머지는 어둡게 표시하고, 사용된 모든 이미지와 프롬프트(클릭 시 펼침)를 보여준다. 텍스트 분석 단계는 제외하고 이미지 생성 파이프라인만 그린다.

1. 목표 / 비목표

목표

비목표 (YAGNI)

2. 데이터 모델 (탐색 결과 요약)

이미지는 단일 테이블 image_asset(backend/app/models/project.py:150)에 asset_type 디스크리미네이터로 구분된다.

asset_type의미핵심 키
floor_planFP(평면도)entity_id=location
reference인물/소품 레퍼런스entity_id
chain_bg / background_chain_node배경 플레이트/체인 노드episode_id, (location)
composite합성source_image_id
scene씬 스틸still_id, episode_id

참고: "스케치/포즈 가이드/구도 가이드"는 별도 asset_type이 아니라 파이프라인 단계 산출물이다. 이미지 자산으로 저장되는 한 위 타입 중 하나(주로 reference/체인 노드)로 들어오거나 image-time 생성물이다. 실제 저장 여부는 구현 시 확인 후 노드 포함 규칙에 반영한다.

엣지(생성 입력) 소스 — 3계층

소스형태신뢰도
source_image_id / parent_image_id (self-FK)이미지 UUID → 이미지 UUID정확(i2i/편집 체인)
image_asset.reference_image_ids (lineage)이미지 UUID 배열(등장 인물/소품 ref)"의도된" 참조 — 실제 attach와 다를 수 있음
llm_call_log (project.py:281)실제 attach 라벨 + 정확한 프롬프트(user_prompt)SOT(실제 호출) — 단 ⚠️ output 이미지 FK 없음, 참조가 텍스트 라벨

핵심 난점. 파이프라인의 본체인 FP→배경플레이트→씬스틸 체인은 reference_image_ids(인물/소품만)에 담기지 않는다. 또 llm_call_log는 실제 attach의 SOT지만 (a) 산출 이미지로의 직접 FK가 없고(metadata_jsonstill_id/entity_id로 조인), (b) 참조가 이미지 UUID가 아니라 텍스트 라벨("BACKGROUND chain reference" 등)로 저장된다. 따라서 "실제 사용된 연결고리"를 정확히 그리려면 백엔드에서 구조적 조인으로 엣지를 복원해야 한다. (사용자 결정: 정확 복원 채택)

3. 접근안 비교

A. 백엔드 정확 복원 채택B. lineage만(프론트only)C. 생성시 UUID 로깅 추가
엣지 정확도높음(구조적 조인+llm_call_log 검증)낮음(배경/FP 체인 누락)매우 높음(미래 데이터만)
백엔드 변경신규 read-only 엔드포인트 1개없음이미지 파이프라인 코어 수정
기존 데이터복원 가능제한적소급 불가
리스크read-only, 메인 파이프라인 무접촉오해 소지(가짜 정확도)핵심 경로 회귀 위험

채택: A. 읽기 전용 신규 엔드포인트로 기존 데이터에서 엣지를 구조적으로 복원하고 llm_call_log로 실제 attach 여부를 플래그한다. 메인 파이프라인을 건드리지 않아 회귀 위험이 없고, 정확도와 작업량의 균형이 가장 좋다. (C는 추후 별도 개선으로 분리 가능.)

4. 백엔드 설계 (읽기 전용)

엔드포인트

GET /api/v1/projects/{project_id}/pipeline-graph?episode_id=<eid>
  → PipelineGraphResponse { nodes[], edges[], episodes[] }

신규 파일: backend/app/services/pipeline_graph_service.py(조립 로직, read-only), backend/app/schemas/pipeline.py(스키마). 라우트는 기존 backend/app/api/v1/images.py 또는 신규 pipeline.py 라우터(프로젝트 prefix 재사용).

Node 스키마

{
  id, asset_type, status,
  entity_id, entity_short_id, entity_name, entity_type,   // 그룹/색상용
  still_id, scene_index, shot_index, episode_id,
  variant_label, width, height, created_at,
  thumb_url:  /api/v1/projects/{pid}/images/{id}/file?thumb=1,
  full_url:   /api/v1/projects/{pid}/images/{id}/file,
  prompt:     image_asset.prompt_used        // 노드 표시용
}

Edge 스키마 & 복원 규칙 (구조적 조인 — 글자/substring 의미파싱 금지)

kind복원 규칙(구조 키)source→target
i2isource_image_id / parent_image_id 직접부모 이미지 → 파생 이미지
reference씬 노드의 reference_image_ids(lineage, 자산 UUID)인물/소품 ref → 씬
background(episode_id, location, scene/still) 구조 조인으로 그 씬의 배경 플레이트 자산 매칭 [구현시 background_chain_render_step 키 확인]배경 플레이트 → 씬
fp같은 location(entity_id)+episode의 floor_plan → chain_bgFP → 배경
prev_scenescene_still.dependent_scene_id → 직전 씬 대표 이미지이전 씬 → 현재 씬

각 엣지에 actual_attached: bool 플래그를 붙인다 — llm_call_logmetadata_json(still_id/entity_id)+operation_type으로 조인해 실제 호출에 attach됐는지 검증. 미확인 엣지는 점선/저채도로 구분 표시(가짜 정확도 방지, 정직성). 엣지별 prompt_authoritative(= llm_call_log.user_prompt)는 노드 상세에서 선택적으로 노출.

규칙 준수 라벨 텍스트의 글자/substring 매칭으로 의미를 추출하지 않는다. 오직 구조 키(episode_id·entity_id·still_id·location short_id·asset_type·created_at 순서)로 조인한다. llm_call_log.reference_image_ids 라벨은 검증 플래그 용도로만 쓰고 엣지 생성의 1차 소스로는 쓰지 않는다.

5. 프론트엔드 설계

라이브러리

@xyflow/react (React Flow v12) 추가 — 노드-링크 캔버스, 팬/줌, 커스텀 이미지 노드, 선택/하이라이트/디밍을 노드·엣지 스타일로 제어. React 19 호환. 레이아웃 좌표 계산은 dagre(레이어드 DAG)로 1회 계산. (대안: SVG+d3-zoom 수제 — 팬/줌/드래그/레이아웃 재구현 부담 커서 비채택.)

노드 색상(엔티티 타입 기준):
location/FP·배경 character ref prop ref scene 스틸 composite

레이아웃

레이어드 DAG(좌→우 또는 상→하): FP/ref(소스)배경 플레이트씬 스틸합성(싱크). dagre로 좌표 계산 후 React Flow에 주입.

커스텀 노드

썸네일(?thumb=1, lazy-load) + 타입 배지 + 엔티티/씬 라벨. 디밍 상태 = opacity 0.15.

인터랙션

추가/변경 파일

파일변경
frontend/src/pages/PipelineCanvas.tsx신규 페이지
frontend/src/hooks/api/usePipelineGraph.ts신규 react-query 훅
frontend/src/components/pipeline/PipelineNode.tsx, NodeDetailPanel.tsx, layout.ts신규
frontend/src/App.tsx라우트 추가(/projects/:id/pipeline)
frontend/src/components/layout/Sidebar.tsx프로젝트 메뉴에 항목 추가
frontend/src/i18n/ko.jsonnav.project.pipeline 라벨
frontend/package.json@xyflow/react, dagre(+@types/dagre) 추가

6. 확인이 필요한 해석 포인트

  1. 하이라이트 범위: 노드 클릭 시 직접 입력 노드만 밝게 할지, 상류 전체 조상 체인을 밝게 할지. "연결고리"/"파이프라인 전체"라는 표현상 상류 전체를 기본으로 본다(FP→배경→씬 체인을 따라가며 보여줌). 직접 입력만 원하면 1-hop으로 조정.
  2. 엔티티 관계 표현: "각 노드는 모두 이미지"이므로 엔티티를 별도 노드로 만들지 않고, 이미지 노드를 엔티티별 색상/그룹(스웜레인)으로 묶고 참조 엣지로 관계를 읽게 한다. 별도 엔티티 노드/관계선이 필요하면 알려주세요.
  3. 씬 변형(variation): 한 씬에 technique_1/technique_2 + 앵글 등 여러 스틸 변형이 있다. 모두 개별 노드로 펼칠지, 대표 1개로 접고 펼침 토글을 줄지. 기본은 모두 개별 노드(전체 구조 표시 취지).

7. 검증 / 완료 기준

8. 단계 계획(개략)

  1. 백엔드: 엣지 복원 조인 규칙 확정(background_chain_render_step 키 확인) → pipeline_graph_service + 스키마 + 엔드포인트.
  2. 결정론 단위 테스트(엣지 복원) + 실데이터 응답 육안 검증.
  3. 프론트: React Flow 캔버스 + dagre 레이아웃 + 커스텀 노드 + 클릭 하이라이트/디밍 + 사이드 패널.
  4. 메뉴/라우트/i18n 추가.
  5. 실데이터 E2E(에피소드 1개) 렌더 → 육안 리뷰(외부 웹서버) → Codex 리뷰 → 수정 → 커밋(사용자 GO 시).

9. Codex 리뷰 반영 (2026-06-29) — A안 승인 + 보강

Codex가 설계서 + 관련 코드(load_background_chain_bg_map, scene_generation_coordinator.build_scene_attached_refs, background_render_step._register_image_assets 등)를 직접 추적해 A안을 승인하고 다음을 보강했다. 아래 내용이 §2·§4·§5·§6 대비 우선한다.

9.1 배경 엣지 복원 — 정확한 소비자 키 (구조 필드, 라벨 파싱 아님)

소비자 SOT는 load_background_chain_bg_map. 우선순위: background_render Phase 7 → background_chain_render Phase 5 → legacy.

엣지 복원 순서(확정):

  1. checkpoint background_render.data.groups를 1차 SOT로 bg_id → png_path/location_id/shot_ids 읽기.
  2. DB image_asset에서 asset_type='chain_bg' AND episode_id AND variant_type=bg_id로 노드 매칭. 없으면 png_path 기반 virtual node(node_origin='checkpoint_virtual' 표기 — DB sync miss 숨기지 않음).
  3. shot_ids로 scene still (scene_index, shot_index)에 bg→scene 엣지.
  4. Phase 7 없으면 Phase 5 background_chain_render.data.groups → legacy data.locations[loc_id].shot_backgrounds[] fallback.
  5. FP→bg: floor_plan ImageAsset의 entity_id == bg.location canon으로 연결. 실제 render가 floor_plan ref를 썼는지 catalog/ref_used 있으면 표시, 없으면 inferred.

주의(과장 방지): bg_map 매핑이 있어도 close framing / dep-scene continuity로 실제 attach에서 background_chain_ref가 skip될 수 있다. bg→scene 엣지는 planned_by_bg_map=trueverification_status를 분리하고, planned-only는 점선으로 표시.

9.2 엣지 신뢰도/검증 표기 (call_log는 검증·라벨 표시, UUID SOT 아님)

각 엣지에 두 축의 메타를 단다 — actual_attached=true 같은 단정 대신 정직한 표기 사용.

9.3 dual-source 노드 — ImageAsset + checkpoint virtual (★"모든 노드=이미지" 충족 핵심)

중요 보정. 가이드/스케치류(outdoor composition guide, registered pose guide, visual_continuity_anchor sketch 등)는 image_asset row가 아니라 checkpoint 경로에만 존재할 수 있다. "모든 노드=이미지"(FP·스케치 등 전부) 요구를 만족하려면 노드 소스를 이중으로 둔다:

MVP에서는 ImageAsset 노드를 먼저 완성하고, virtual 노드(가이드/스케치)는 곧바로 후속 단계로 포함한다 — 단 설계상 이중 소스를 처음부터 전제한다(서비스 인터페이스가 두 소스를 합쳐 nodes[]를 만들도록).

9.4 하이라이트 / 필터 (확정)

9.5 §6 해석 포인트 정리

9.6 구현 전 문서 보강 체크리스트 (Codex 요약)

  1. background 엣지 키 = background_render/background_chain_render.data.groups[bg_id].shot_ids 중심 — §9.1 반영 완료.
  2. chain_bg.variant_type=bg_id DB 매칭 + checkpoint virtual fallback — §9.1/9.3 반영 완료.
  3. call_log = 검증/라벨 표시이지 UUID edge SOT 아님 — §9.2 반영 완료.
  4. 가이드/스케치 virtual nodes — §9.3 반영 완료.

— 끝. Codex A안 승인 + 보강 반영 완료. 사용자 승인(또는 goal 진행 지시) 후 writing-plans로 구현 계획서(HTML)를 작성하고 개발한다.