Skip to main content

Storage and Data Integrity

1. 원칙

Cinema는 원문을 임의의 바이너리 포맷으로 압축해 애플리케이션에서 직접 관리하지 않는다. PostgreSQL은 큰 text 값을 TOAST로 외부화하고 필요하면 압축하므로 줄바꿈을 제거하거나 gzip blob으로 바꾸면 검색, 부분 추출, 디버깅과 마이그레이션 비용만 커진다.

저장 효율화의 우선순위는 같은 논리 본문을 여러 행에 복사하지 않는 것이다.

2. 시나리오 본문 계약

  • 전체 본문은 불변 CinemaScriptRevision.contentSnapshot에 한 번 저장한다.
  • SequenceProposal, Sequence, SceneProposal, Scene, BeatProposal, BeatscriptRevisionId, sourceStart, sourceEnd만 저장한다.
  • sourceExcerpt는 저장 컬럼이 아니며 상세 API가 contentSnapshot.slice(sourceStart, sourceEnd)로 복원하는 파생 필드다.
  • sourceStartsourceEnd는 JavaScript 문자열 인덱스 계약을 사용하고 같은 Revision 안에서만 해석한다.
  • originalSnapshot에도 원문 발췌를 중복 저장하지 않는다.
  • Shot은 Script 원문 범위를 복제하지 않고 Scene과 Beat Mapping만 참조한다.

이 구조는 줄바꿈과 공백을 원문 그대로 보존하면서도 Sequence·Scene·Beat 수에 비례해 본문 사본이 늘어나는 문제를 제거한다.

3. ScriptRevision 중심 저장

별도 ScriptDraft 테이블은 사용하지 않는다. 편집 중 내용은 클라이언트 로컬 상태이고, 저장 확인 시 다음 필드의 ScriptRevision을 직접 생성한다.

id, projectId, version, contentSnapshot,
contentHash, characterCount, createdBy, createdAt

클라이언트는 마지막으로 본 최신 Revision의 ID를 expectedRevisionId로 보낸다. 서버의 최신 ID와 다르면 저장을 거절한다. 같은 content hash를 다시 저장하는 요청은 새 version을 만들지 않는다.

4. Proposal 데이터 분리

Proposal의 세 가지 축은 서로 다른 필드다.

책임
검토 상태PENDING, ACCEPTED, REJECTED사용 여부
생성 출처AI, MANUAL, SPLIT, MERGED생성 경로
사용자 편집hasUserChanges사람이 내용을 바꿨는지

originalSnapshotorigin = AI일 때만 존재한다. 수동·분할·병합 결과에는 현재 필드와 동일한 snapshot을 다시 저장하지 않는다.

5. 목록과 상세 조회

  • Project 목록은 제목, 로그라인, 장르, 최신 Revision 글자 수와 Revision 수만 조회한다.
  • Revision 목록은 contentSnapshot을 선택하지 않고 저장된 characterCount를 사용한다.
  • ProposalSet 목록은 상태, Profile, 모델, 생성 시각 등 이력 선택에 필요한 필드만 조회한다.
  • Sequence·Scene 목록은 카드 표시용 필드만 조회하며 원문 발췌를 복원하지 않는다.
  • 상세 API에서만 Revision 본문과 필요한 관계를 로드한다.

페이지 수가 커지면 cursor pagination을 추가하되, 현재 MVP는 응답 필드 축소를 우선 적용한다.

6. DB 무결성

DB는 다음 규칙을 직접 강제한다.

  • ProposalSet과 Sequence의 projectId + scriptRevisionId가 실제 Revision 범위와 일치한다.
  • Scene과 Scene ProposalSet의 Project·Revision이 상위 Sequence 범위와 일치한다.
  • source range는 sourceStart >= 0, sourceEnd > sourceStart다.
  • 모든 orderIndex는 0 이상이다.
  • Sequence 분석 ProposalSet은 source가 없고, Scene 분석은 sourceSequenceId, Beat·Shot 분석은 sourceSceneId만 가져야 한다.
  • AI Proposal만 originalSnapshot을 가진다.
  • 한 Project에는 Sequence PROCESSING 분석이 하나만, 한 Sequence에는 Scene, 한 Scene에는 유형별로 Beat와 Shot PROCESSING 분석이 하나씩만 존재할 수 있다.
  • Beat의 Project·Revision이 상위 Scene 범위와 일치하고 sourceProposalId는 중복될 수 없다.
  • 정식 Beat-Shot Mapping은 (beatId, sceneId)(shotId, sceneId) 복합 FK로 양 끝이 같은 Scene임을 보장한다.
  • 같은 Shot-Beat 쌍은 중복될 수 없고 coverage 시작·종료의 동시 null 또는 유효한 양수 범위를 CHECK로 보장한다. Shot 길이 상한 검사는 트랜잭션 발행 로직에서 수행한다.

Revision 본문의 실제 길이를 넘는 범위와 분할·병합의 연속성은 다른 행의 본문을 읽어야 하므로 서비스 트랜잭션에서 추가 검증한다.

7. 현재 테이블

테이블주된 책임
cinema_projects영화 메타데이터와 소유자
cinema_script_revisions불변 시나리오 본문 버전
cinema_proposal_sets분석 실행과 모델 메타데이터
cinema_sequence_proposalsSequence 후보와 source range
cinema_sequences승인된 Sequence
cinema_scene_proposalsScene 후보와 source range
cinema_scenes승인된 Scene
cinema_beat_proposalsScene 내부 Beat 후보, 퍼포먼스와 source range
cinema_beats발행된 Beat와 연출 메모, 활성·보관 상태 및 MANUAL·SUPERSEDED 보관 사유
cinema_shot_proposals카메라·렌즈·구도·움직임·예상 길이를 가진 Shot 후보
cinema_shot_proposal_beat_mappings검토 중 Shot과 활성 Beat의 M:N 표현 관계
cinema_shots발행된 Shot과 연출 메모, 활성·보관 상태
cinema_beat_shot_mappings발행된 Shot과 Beat의 동일 Scene M:N 관계
assets사용자별 공용 원본 정체성. 현재 Persona·Coordi source를 다형 참조
asset_versions연결 시점 원본의 불변 snapshot과 checksum
cinema_asset_foldersProject 내부 Binding 분류용 가상 폴더
project_asset_bindingsProject와 고정 AssetVersion의 연결 및 프로젝트별 메타데이터

기존 personas, coordis는 사용자가 편집하는 원본 테이블이고 assets는 해당 원본의 공용 정체성, asset_versions는 Cinema가 재현성을 위해 고정한 snapshot이다. assets.sourceId는 타입별 원본 테이블을 가리키는 다형 참조이므로 DB FK 대신 서비스가 kind별 접근 권한과 존재 여부를 검사한다.

8. 중복 저장 점검 결과

현재 Cinema 테이블의 반복 데이터는 다음 기준으로 분류한다.

데이터판단처리
Sequence·Scene·Beat의 원문 발췌불필요한 중복저장하지 않고 Revision과 offset으로 복원
별도 ScriptDraft 본문불필요한 중복테이블 제거, 저장 시 Revision 직접 생성
수동 Proposal의 originalSnapshot불필요한 중복저장하지 않음
AI Proposal의 originalSnapshot비교·감사용 중복AI origin에만 제한 저장
AssetVersion.snapshot의 원본 속성재현성을 위한 필수 snapshot불변 저장, 동일 checksum Version 재사용. 수정 시각은 별도 컬럼에만 저장
AssetVersion.displayName, previewS3Path목록 조회용 projectionsnapshot을 매번 해석하지 않도록 제한적 중복 허용
Binding의 Asset 식별자파생 가능한 중복assetVersionId만 저장하고 assetId는 중복 저장하지 않음
ProposalSet 모델·prompt·schema 정보실행 재현성과 비용 감사분석 실행 단위로 유지

애플리케이션 수준 gzip이나 줄바꿈 제거는 적용하지 않는다. 큰 본문과 JSONB의 물리 압축은 PostgreSQL에 맡기고, 논리 중복 제거·요약 조회·checksum 재사용을 우선한다.

SYSTEM Persona·Coordi의 Asset 정체성은 소비자가 아니라 원본 소유자를 기준으로 생성하므로 여러 사용자가 같은 공용 Version을 재사용한다. 개인 원본은 접근 검사상 소유자 본인만 연결할 수 있다.

한 Project에서 같은 Asset의 Version이 중복 연결되지 않도록 프로젝트 행 잠금 안에서 AssetVersion.assetId를 통해 기존 Binding을 먼저 조회한다. 이를 DB unique index 하나로 강제하려면 Binding에 assetId를 다시 저장해야 하므로, 현재는 불필요한 식별자 중복을 만들지 않고 서비스 트랜잭션 불변식으로 관리한다.

9. Shot과 Prompt 저장 원칙

  • 현재 제작 기준은 CinemaShot, Beat Mapping과 Character Mapping이다.
  • FrameSpec·MotionSpec·Render Plan·Context Run·Generation Attempt 전용 테이블은 사용하지 않는다.
  • CinemaShotPromptPackage는 Shot별 현재 Frame Prompt와 Motion Prompt만 저장한다.
  • 전체 Script 본문, Asset 원본 snapshot 또는 Provider 원본 응답을 Package에 복제하지 않는다.
  • Package에는 생성 당시 shotDefinitionVersion, compiler 계약인 promptVersion, provider·model 실행 메타데이터를 저장한다.
  • 오래된 비동기 결과는 현재 Shot definitionVersion과 일치하지 않으면 반영하지 않는다.

상세 필드와 생명주기는 Shot Prompt Package를 따른다.