Beat 기반 스토리보드
기준일: 2026-09-10. 로컬 구현 기준이며 운영 배포는 수행하지 않았다.
제품 결정
필수 제작 흐름을 시나리오 → 시퀀스 → 씬 → Beat → 스토리보드로 바꾼다. 먼저 여러 Beat를 연속 Shot으로 묶은 뒤 전체를 한 장으로 요약하면 대사와 감정 변화가 한 이미지에 압축되는 문제가 있었다.
Beat마다 기본 시작 카드를 준비하고 필요하면 선택적인 종료 프레임 또는 같은 Beat의 추가 카드를 만든다. 카드의 의미는 한순간의 정지 이미지다. 웃음의 시작과 웃음이 멎은 상태를 각각 표현하려면 두 카드를 사용한다. 카드별 AI 제안은 선택 사항이며 직접 프롬프트를 작성할 수도 있다. 기본 카드 준비와 빈 카드 추가는 LLM을 호출하지 않는다.
기존 ?view=shots의 Shot 계획, Frame/Motion Prompt와 생성 이력은 보존한다. 새 기본 경로는 ?view=storyboard다. 기존 Shot 설계는 스토리보드 화면의 보조 링크에서 접근하며 새 카드 생성에 Shot 승인·재설계를 요구하지 않는다.
화면과 프롬프트
원본 Beat 대본은 기본으로 펼쳐져 있으며 사용자가 접을 수 있다.
화면에서는 스토리보드 카드, 카드에 담을 장면, 카드 추가, 프레임 프롬프트 제안이라는 문구를 사용한다. 사용자에게 각 카드의 내용을 자연스럽게 안내하며, 카드마다 정지 이미지 하나를 표현하는 생성 규칙은 유지한다.
- 왼쪽에서 Beat와 그 안의 카드를 선택한다. 카드 제목, 담을 순간, 같은 Beat 안의 순서를 수정하고 카드를 보관·복원할 수 있다.
- 카드의 순간을 비워 두면 AI가 해당 Beat에서 한순간을 선택한다. Beat 전체나 인접 Beat의 모든 행동을 요약하는 지시는 사용하지 않는다.
- AI 프롬프트 입력은 연결된 Beat 하나, 선택한 순간, 씬의 장소·시간, Beat에서 명시한 등장인물 정보로 제한한다. 한 시점에 보이는 자세·시선·표정을 기술하며 시간의 진행이나 여러 패널을 만들지 않도록 한다.
- 구조화된 응답은
framePrompt와 빈motionPrompt를 요구한다. 카드 화면에는 모션·영상 길이 입력을 표시하지 않는다. - 참조 이미지는 기본 접힘/+ 토글, 썸네일 상세보기·삭제, 프로젝트 페르소나·코디 선택, @ 목록·실제 입력 순서 매핑을 재사용한다.
- 씬 배경 상속, 카드별 이미지·월드 선택과 시점 캡처를 재사용한다. 프로젝트의 화면비·해상도를 그대로 적용한다.
- 카드마다 여러 생성 후보를 보존하되 보드에 사용할 대표 이미지는 한 장만 선택한다. 보드 모아보기는 Beat 순서와 카드 순서를 따른다.
- 카드·프롬프트 미저장 상태에서 카드 전환과 링크 이동을 확인한다. 동시 변경 충돌 시 초안을 유지하고 저장된 버전을 다시 불러올 수 있다.
저장 및 호환 계약
새 카드도 기존 생성·참조·배경·결과 선택 서비스를 재사용한다. 물리적으로 cinema_shots에 storyboardBeatId와 storyboardMoment가 있는 행으로 저장하며, storyboardBeatId IS NULL인 기존 Shot 계획과 구별한다. 이 저장 방식은 사용자에게 Shot을 먼저 만들도록 요구한다는 뜻이 아니다.
- 카드와 Beat는 같은 Scene으로 제한하는 복합 외래 키로 연결한다. 카드의 Beat 매핑은 한 개다.
- 기존 Shot 목록, Shot 계획 승인·보관은 카드 행을 제외한다. Beat 계획을 대체하면 과거 Beat 기반 카드도 보관 이력에 남는다.
- 쓰기는 프로젝트 소유권, 활성 Beat·Scene·Sequence와 최신 시나리오 버전을 검증한다. 과거 Beat의 카드와 결과는 읽기 전용이다.
storyboardEditVersion은 카드 편집·순서·보관의 동시 변경 검사에 사용한다.definitionVersion은 제목·순간 내용이 바뀔 때만 증가하므로 단순 정렬로 생성 이미지가 오래된 내용으로 표시되지 않는다.- 카드 내용 변경은 진행 중인 프롬프트 제안의 저장 버전을 무효화한다. 수동 프롬프트와 이미지 원본을 자동으로 지우거나 다시 생성하지 않는다.
- 실제 생성 Snapshot에는 기존 전체 Prompt·참조·설정·배경에 더해
storyboardCardId,beatId,storyboardMoment를 보존한다. - 마이그레이션
1789787400000은 카드 연결과 순간,1789787500000은 별도 편집 버전을 추가한다. 기존 Shot·이미지를 이동하거나 재생성하지 않는다.
API 기준 경로: /api/v1/cinema/projects/:projectId/sequences/:sequenceId/scenes/:sceneId/storyboard
| 메서드·경로 | 동작 |
|---|---|
| GET | Beat와 카드 목록·보관 이력 조회 |
| POST /initialize | 카드가 한 번도 없었던 활성 Beat에 기본 카드 준비; 재요청으로 중복 생성·보관 카드 복원하지 않음 |
| POST | beatId에 추가 카드 생성 |
| PATCH /:cardId | expectedVersion, 제목·순간 또는 UP/DOWN/ARCHIVE/RESTORE |
프롬프트·이미지 생성·결과·배경 API는 기존 Shot 경로에 카드 ID를 전달해 사용한다. 기존 선택 결과의 원본 삭제 보호와 참조 ID 비재사용 규칙도 유지한다.
검증과 남은 범위
- Backend 런타임 TypeScript, Frontend TypeScript와 변경 범위 ESLint를 확인했다.
- Beat별 기본 카드 준비의 멱등성, 추가 카드, 소유권·과거 Beat·버전 검사, 보관·복원, 순서와 내용 버전 분리, 카드당 대표 이미지 한 장, 단일 Beat 프롬프트 입력, 빈 모션 구조화 응답을 테스트했다. 기존 Beat/Shot 승인, 프레임·참조·공통 설정·월드 관련 테스트도 실행했다.
- 기존 테스트 Scene의 7개 Beat에서 기본 카드 준비와 추가 카드 편집·프롬프트 저장, 프로젝트 에셋과 @ 입력, 카드 순서 변경·보관·복원, 기존 승인 Shot 두 개의 보존을 브라우저에서 확인했다. 기본 카드 7개는 사용할 수 있도록 남기고 별도로 만든 검증용 추가 카드는 보관한다.
- 유료 LLM·이미지·영상 생성은 실행하지 않았다. 실제 모델의 한순간 표현 품질과 이미지 출력은 사용자의 다음 생성 테스트에서 확인한다.
- 스토리보드 카드를 선택해 연속 영상 구간으로 묶는 새 편집 UI, 영상 생성 실행과 카메라 경로는 후속 범위다. 기립 더미 배치는 아래 2026-09-09 구현 기록을 참고한다. 기존 Shot 이력 접근을 이 영상 편집 기능의 완료로 간주하지 않는다.
이 결정은 앞선 0~3차 계획의 필수 Shot 단계와 Shot 단위 스토리보드 부분을 대체한다. 이전 구현 기록은 당시 이력으로 남긴다.
커밋
- Backend
639eaff: Beat 카드 저장·API·단일 순간 프롬프트와 회귀 테스트. - Backend
ed3582d: 카드 순서와 이미지 내용 버전 분리. - Frontend
e6348db8: Beat 스토리보드 기본 경로·카드 편집·재사용 생성 UI·저장 충돌 복구.
2026-09-09 월드 캡처 입력 경로 수정
월드 캡처 업로드가 반환하는 HTTPS S3 URL과 생성 정책의 s3:// 입력 요구가 달라 프레임 생성이 거부되던 오류를 수정했다. 새 캡처는 S3 경로로 등록하며 이미 저장된 HTTPS 캡처·참조도 생성 직전에 알려진 S3 URL만 정규화한다. 전체 생성 Snapshot에는 실제 전송 경로를 남긴다. 기존 캡처를 다시 만들 필요는 없다.
임의 외부 URL 허용으로 생성 정책을 완화하지 않는다. 경로 표현만 달라진 참조는 생성 이력의 입력 변경으로 오인하지 않도록 비교 시에도 정규화한다. S3 URL 변환·기존 캡처·허용하지 않는 URL 차단·입력 이력 비교 테스트를 통과했고 유료 이미지 생성은 실행하지 않았다.
2026-09-09 GPT 이미지 생성 크기 정렬
프로젝트의 제작·월드 캡처 크기와 모델에 전달하는 생성 크기를 구분한다. GPT OpenAIGPTImageNodeV2의 Custom width/height로 매핑한 이미지 워크플로우에서는 화면비를 정확하게 유지하면서 두 변을 16의 배수로 올림한다. 예를 들어 16:9 FHD 제작 설정은 1920×1080으로 유지하고 생성 요청은 2048×1152로 전달한다. 기존 캡처를 다시 만들거나 자동으로 리사이즈하지 않는다. 생성 결과를 최종 납품 크기로 변환하는 작업은 이번 수정에 포함하지 않는다.
워크플로우의 크기 매핑·최대값·step·Custom 설정 검증은 유지하며 다른 노드 및 영상 설정에는 이 보정을 적용하지 않는다. Snapshot의 cinemaProduction.settings에는 제작 설정을, generationDimensions에는 실제 요청 크기를 기록하고 시나리오의 생성 설정 기록에 표시한다. 현재 설정과의 변경 여부 비교에는 기존 제작 설정을 사용한다.
최종 워크플로우 매핑, 5개 화면비 × 3개 해상도의 배수·비율·하한 유지, 허용 범위 검사, Snapshot과 기존 월드 캡처 회귀 테스트를 확인했다. 실제 유료 이미지 생성은 실행하지 않았다.
2026-09-09 씬 배경과 카드 적용 상태 구분
실제 생성 기록에서 씬에 월드 캡처가 저장되어 있지만 카드의 NONE 개별 설정 때문에 environment: NONE, 빈 참조 목록으로 생성되는 사례를 확인했다. NONE은 씬 배경 상속이 아니며 INHERIT를 선택해야 공통 배경을 사용한다.
배경 편집 화면에 이번 프레임 배경을 항상 표시하고, 씬 배경과 다른 개별 설정에는 공통 배경을 현재 카드에도 적용하는 버튼을 제공한다. 씬 공통 배경을 편집하는 화면과 실제 카드 적용 상태를 구별하며, 씬 배경이 있는데 카드가 NONE이면 생성 버튼을 배경 없이 프레임 생성으로 표시한다.
배경 선택과 캡처 업로드·선택 저장을 공용 Mutation 상태로 추적한다. 업로드부터 배경 저장 완료까지 생성 버튼을 비활성화하고, 캡처를 시작한 시점의 대상 범위와 변경 버전을 고정한다. 월드만 선택하고 캡처하지 않은 경우에도 생성을 비활성화한다. 기존 생성 결과는 자동 재생성하지 않는다.
문제 카드에 기존 씬 캡처의 상속을 적용하고 브라우저에서 실제 배경과 참조 썸네일을 확인했다. 타입·범위 lint와 기존 배경/캡처/생성 요청 테스트를 확인했으며 유료 재생성은 실행하지 않았다.
2026-09-09 사용자 배치 지시와 월드 구도 유지
카드 설명은 시간 범위뿐 아니라 인물·행동·카메라 기준 위치·거리의 연출 지시로 사용한다. 대본의 장소 이름에서 추론한 배치보다 사용자의 명시적 배치를 우선하며, 불필요한 손짓·동기·소품·휴대폰 글자 제한 등을 추가하지 않도록 한다. 카드 설명에 직접 명시한 인물도 프롬프트 제안의 캐릭터 문맥에 포함한다.
제안 요청에는 실제 적용 배경이 월드 캡처인지 여부를 전달한다. 제안은 텍스트 입력이므로 캡처를 직접 본 것처럼 건물 구조를 추측하지 않는다. 최종 이미지 프롬프트에는 월드 캡처의 실제 입력 번호를 가리키는 WORLD VIEWPOINT 지시를 추가해 시선 방향·원근·프레이밍·건축물 배치를 유지하고 카메라 기준 위치에 인물을 넣도록 한다. 일반 참조 이미지에는 이 고정 시점 지시를 강제하지 않는다.
기존 저장 프롬프트와 결과는 자동으로 덮어쓰지 않는다. 새 규칙을 사용하려면 프롬프트 제안을 다시 요청하거나 사용자가 직접 수정한다. 참조 순서와 위치 지시 전달·명시된 인물·텍스트 제안의 배경 모드 회귀 테스트를 확인했으며 모델의 실제 출력 품질은 유료 재생성을 실행하지 않아 미확인이다.
2026-09-09 월드 내 등장인물 더미 배치
스토리보드 카드의 배경 선택 → 현재 카드만 → 월드 시점 선택·복원에서 프로젝트 캐릭터를 더미로 배치한다. 프로젝트 에셋에서 페르소나와 연결된 활성 캐릭터가 선택 목록에 나타난다.
- 단순 기립 더미를 추가하고 바닥 클릭 또는 X/Y/Z 입력으로 위치, 슬라이더로 방향, 미터 입력으로 키를 조정한다. 얼굴 앞면 표식과 번호로 방향·인물을 구별한다. 기본 조작은 바닥 클릭 배치이며 더미를 직접 누르면 해당 인물이 선택되고 드래그로 이동한다. 카메라 이동 모드로 전환하거나 개별 더미를 삭제할 수 있다.
- 처음 추가하는 위치는 카메라 앞 3m, 발 높이는 카메라 아래 1.65m로 추정한다. 바닥은 선택한 Y 높이의 수평면이며 SPZ 충돌 메시나 자동 지형 검출이 아니다. 실제 바닥과 다르면 높이를 조정한다.
- 화면 밖 더미가 있으면 캡처를 차단한다. 미저장 배치가 있으면 프레임 생성을 막고 카드 전환·페이지 이탈 시 기존 미저장 변경 확인 흐름에 포함한다.
배치·시점 저장은 같은 카메라·공통 해상도로 깨끗한 월드 배경과 번호가 붙은 더미 가이드 두 PNG를 만든다. 깨끗한 배경은 배경 선택 영역에서 미리 볼 수 있고, 생성 참조 썸네일에는 실제 입력되는 가이드만 표시한다.
저장과 생성 계약
별도 테이블이나 기존 데이터 이동 없이 캡처 Creation의 snapshot.worldCapture.blocking에 version, projectId, cardId, guidePath, actors를 저장한다. 각 actor는 캐릭터 ID, 위치·방향·키, 서버에서 확정한 캐릭터 이름·AssetVersion·원본 이미지 경로를 갖는다. 기존 worldCapture의 WorldRevision·Artifact·카메라·공간 변환·해상도도 함께 보존한다.
캡처 선택은 기존 배경 변경 버전 검사로 저장한다. 현재 카드가 직접 선택한 캡처이고 저장된 projectId/cardId가 일치할 때만 배치를 적용한다. 씬 공통 배경 상속이나 다른 카드에서 같은 배경을 재사용할 때는 더미 배치를 가져오지 않는다. 씬 공통 배경 편집에는 인물 배치 도구를 표시하지 않는다. 배치를 비우고 다시 저장하면 가이드와 자동 캐릭터 참조도 제거된다.
최종 입력 순서는 다음과 같다.
- 더미가 있으면 배경이 포함된 배치 가이드 한 장, 없으면 깨끗한 배경 한 장
- 더미가 있을 때 각 더미와 연결된 페르소나 원본 이미지
- 사용자가 추가한
참조N이미지
최종 프롬프트는 가이드의 더미 번호와 캐릭터 이름, 실제 이미지 입력 번호를 연결한다. 가이드는 배경·시점과 위치·화면상 크기·방향·깊이를, 페르소나는 외형을, 텍스트는 동작·표정을 제공한다. 캐릭터 시트의 여러 뷰는 한 인물이며 더미 색·번호는 결과에 그리지 않도록 명시한다. 캐릭터 멘션은 인물(이름)으로 유지해 다른 더미 삭제로 번호가 바뀌어도 이름이 바뀌지 않는다. 삭제된 캐릭터 멘션이나 존재하지 않는 참조N은 생성 전에 오류로 알린다.
가이드 또는 배경 한 장과 캐릭터·일반 참조를 워크플로우 입력 한도에 포함한다. 배치 자체는 최대 14명이며 16개 입력 워크플로우에서 가이드 1 + 인물 14를 연결하면 추가 참조 한 장을 더 사용할 수 있다. 연결된 워크플로우 한도가 작으면 그 한도에 맞춰 인물 또는 추가 참조를 줄여야 한다. 캡처 변경은 다른 결과 ID로 저장되어 기존 생성 이력의 입력 변경 판정에 반영된다.
검증 및 범위
- Backend 관련 5개 suite, 26개 테스트: 좌표·중복·한도 검사, 프로젝트 캐릭터 연결, 카드/프로젝트/씬 상속 격리, 캡처·가이드 크기와 출처, 실제 입력 순서·참조 별칭·워크플로우 한도, 생성 이력 비교. 추가로 삭제된 캐릭터 멘션 검사도 확인했다.
- Backend 런타임 TypeScript, Frontend TypeScript 및 변경 범위 ESLint 통과.
- 브라우저에서 두 인물 배치, 좌표 변경·클릭 배치·방향 조절·삭제, PNG 저장, 새로고침 후 복원, 가이드·배경 각각의 미리보기, 기존 카드로 배치가 전파되지 않는 것을 확인했다. 배경은 실제 1920×1080이며 더미가 포함되지 않는다.
월드 인물 배치 검증카드를 별도로 만들어 검증 후 보관했다. 검증 과정의 배경 캡처 2장과 연결된 가이드가 남아 있으며 기존 카드·프롬프트·생성 결과는 수정하지 않았다.- 유료 LLM/이미지/비디오 생성 및 운영 배포는 수행하지 않았다. 모델의 실제 위치·외형 재현 품질은 다음 생성 테스트에서 확인한다. 포즈 라이브러리, 리깅·애니메이션, 충돌 메시·자동 바닥 맞춤은 포함하지 않는다.
구현 커밋: Backend dc16c19, Frontend d32f83c7. 로컬 커밋이며 원격 push는 수행하지 않았다.
배경·가이드 입력 통합 및 연결 해제
더미가 있으면 배경을 포함한 가이드만 생성에 전달하고 깨끗한 배경은 저장·미리보기용으로 보존한다. 더미가 없으면 배경만 전달한다. 기존 캡처를 다시 저장하지 않아도 다음 생성부터 적용된다. 가이드의 배경·시점을 유지하면서 번호별 더미를 연결된 인물로 교체하도록 최종 프롬프트를 정리했다. 참조 매핑과 입력 한도는 실제 전송하는 목록을 기준으로 계산한다.
참조 썸네일과 펼친 참조 영역에서 배경 또는 가이드의 ×로 현재 카드의 연결을 해제한다. 가이드를 해제하면 배치에서 자동 연결된 인물 참조도 함께 빠진다. 개별 더미 삭제는 월드 편집기에서 수행하고 저장한다. 원본 캡처·가이드·프로젝트 에셋·씬 공통 배경은 삭제하지 않는다. 상속 중인 배경을 해제해도 현재 카드만 NONE으로 저장하며 일반 추가 참조는 유지한다. 저장 중이거나 미저장 배치가 있으면 참조 삭제를 잠근다.
관련 테스트 3개 suite/20개, Backend/Frontend TypeScript와 변경 범위 ESLint 통과. 기존 검증용 카드를 잠시 복원해 가이드·자동 인물 참조의 동시 해제, 상속 배경의 카드별 해제와 씬 공통 배경 보존을 확인한 뒤 다시 보관했다. 유료 생성은 실행하지 않았다.
더미 직접 드래그
기본 바닥 클릭 모드에서 더미(번호 표식 포함)를 누르면 그 인물을 선택한다. 누른 지점과 발의 화면상 간격을 유지해 처음 잡을 때 위치가 튀지 않으며, 드래그 중 바닥 높이·키·방향은 유지한다. 클릭만으로는 더미 위치를 바꾸지 않는다. 인물이 아닌 바닥을 클릭하면 기존처럼 선택한 더미를 해당 위치에 배치한다.
드래그 중에는 카메라 조작을 잠그고 pointerup·pointercancel·캡처 해제·포커스 이탈·뷰어 종료 시 잠금을 해제한다. 다른 포인터는 진행 중인 드래그를 변경하지 않는다. 카메라 이동 모드와 읽기 전용 화면은 더미를 수정하지 않는다. 이동 중에는 새 메시·텍스처를 반복 생성하지 않고 기존 모형의 변환만 갱신한다.
검증: node --no-warnings --experimental-strip-types --test world-blocking-controls.test.mts의 5개 테스트(선택·잡은 지점 유지·높이 유지·포인터 해제·모드 분리·바닥 클릭·다중 포인터), Frontend TypeScript와 변경 범위 ESLint 통과. 브라우저에서 기본 바닥 클릭 모드와 카메라 모드 전환을 확인했다. 기존 캡처나 생성 결과는 변경하지 않았다.
카메라 기준 몸 방향 전달
더미 로컬 +Z를 정면으로 사용한다. 저장된 yaw의 전방 벡터 (sin(yaw), 0, cos(yaw))와 각 더미에서 저장된 카메라로 향하는 수평 벡터로 정면·앞쪽 사선·측면·뒤쪽 사선·후면을 구분한다. 카메라 forward/up의 외적으로 화면 오른쪽을 구해 전방 벡터가 화면 좌우 어디로 향하는지도 전달한다. 월드 좌표의 회전 각도를 그대로 모델에 주거나 특정 엘리베이터를 시선 목표로 추정하지 않는다. 카메라가 더미 바로 위에 있는 경우 불확실한 정면/후면 분류를 생략하고, 유효한 카메라 기록이 없는 이전 데이터는 기존 시각 가이드 지시를 유지한다.
최종 생성 프롬프트의 더미 번호·캐릭터·참조 이미지 매핑 뒤에 해당 인물의 몸 방향 문장을 추가한다. 저장된 배치·몸 방향이 캐릭터 시트의 얼굴 방향보다 우선하며, 별도 고개 돌리기 요청이 없으면 머리도 몸과 같은 방향을 유지하도록 한다. 머리 방향·시선 목표를 별도로 편집하는 기능은 이번 범위에 포함하지 않는다.
더미 앞면에 넓은 어두운 표식을 붙이고 몸의 앞쪽으로 노란 화살표를 표시한다. 화살표는 더미의 회전·키·위치를 따라가며 이동 지시가 아닌 몸 방향을 나타낸다. 배치 가이드에만 포함하고 깨끗한 배경 캡처에서는 제외한다. 최종 결과에는 화살표·색·번호를 그리지 않도록 명시한다.
저장된 카메라와 배치가 있는 기존 캡처도 다음 생성부터 방향 문장이 적용된다. 기존 가이드 PNG는 변경하지 않으므로 화살표까지 이미지 입력에 반영하려면 월드 편집기에서 배치·시점 저장을 다시 해야 한다. 과거 생성의 프롬프트와 결과는 그대로 보존한다.
검증: Backend 4개 suite/32개 테스트(8방향, ±360°, 카메라 회전·화면 가장자리 인물·수직 시점·이전 데이터·실제 큐 프롬프트), Frontend 기존 드래그 5개 테스트, 양쪽 TypeScript와 Frontend 변경 범위 ESLint 통과. 브라우저에서 저장된 도윤 배치 130°를 수정 없이 복원하고 화살표·안내문을 확인했다. 유료 생성은 실행하지 않았으며 실제 모델의 방향 재현 품질은 다음 생성 테스트에서 확인한다.
프레임 시점과 캐릭터 시트 (2026-09-10 수정)
스토리보드 카드의 기본 시점은 해당 Beat의 새 행동·등장이 시작되기 직전 상태다. 이후 접근·대화·손짓을 미리 합치지 않는다. 이 시간 조건은 인원 제한이 아니며 시작 상태에 이미 있는 배경 인물은 카드 설명과 대본에 따라 포함한다. 후속 상태는 종료 프레임이나 시점 직접 지정으로 작성한다.
cinema_shots.storyboardFrame JSONB의 momentMode: START | END | CUSTOM을 유지한다. 기존 저장 키 visibleCharacterIds: UUID[] | null은 호환성을 위해 유지하되 첨부할 캐릭터 시트 선택을 의미한다. null과 [] 모두 별도로 선택한 시트가 없는 상태이며 빈 화면이나 인물 없음 지시가 아니다. 기존 선택·카드 설명·프롬프트·생성 결과는 보존하며 새 마이그레이션은 필요 없다.
- 비트 분석 결과의
characterDirections를 기본 시트 선택에 사용한다. 비트 확정 후 스토리보드 카드 준비/추가 시, 해당 프로젝트의 활성 인물 중 페르소나 이미지가 연결된 인물을 선택한다. 정규 이름을 우선하고 별칭은 한 인물로 명확하게 해석될 때만 사용한다. 중복은 제거하며 기존 설정의 최대 14개 범위를 지킨다. 연결되지 않은 군중·미확인 이름은 자동 선택하지 않는다. - 기본 선택은 새 카드 생성 시 저장한다. 기존 카드와 명시적으로 해제한 선택([])은 초기화를 다시 실행해도 덮어쓰지 않는다. 종료 프레임은 시작 카드의 현재 선택을 복사한다. 캐릭터 시트가 선택되어도 실제 등장 여부는 장면 설명으로 결정한다.
- UI를
첨부할 캐릭터 시트로 변경했다. 선택은 외형 참조만 지정하며 화면 등장 여부나 총인원을 제한하지 않는다. 미선택 인물의 이름을 프레임 프롬프트에 쓰는 것도 허용한다. - 실제 보이는 인물과 군중은 카드 설명·대본의 해당 시점, 최종 프레임 프롬프트로 결정한다. 예를 들어 도윤 시트만 선택해도
여러 퇴근한 사람들이 엘리베이터를 기다린다는 장면을 요청할 수 있다. - 월드 더미의 시트는 저장된 배치와 함께 자동 첨부하고 다른 인물 시트를 추가 선택할 수 있다. 같은 인물은 한 번만 첨부하며 월드 배치에 저장된 시트 버전을 우선한다. 자동 첨부를 해제하려면 더미를 삭제하고 배치를 저장한다. 더미 수는 전체 등장인원 제한이 아니다.
- 프롬프트 제안에는
referenceCharacters로 첨부 시트 명단을 전달한다. 대본/카드에 명시된 다른 인물의 설명도 시트 선택과 독립적으로 전달한다. 선택하지 않은 인물 이름을 이유로 제안·저장·생성을 차단하던 검사를 제거했다. - 최종 이미지 요청에도 시트는 이름에 대응하는 외형 참조라는 규칙을 적용한다. 시트가 없는 인물에게 다른 시트의 얼굴을 재사용하거나, 시트 안의 여러 뷰를 여러 사람으로 복제하지 않도록 한다. 배경 군중도 명시된 장면에 따라 허용한다. 참조 번호/
인물(이름)@태그는 실제 첨부에 대응해야 한다는 검사는 유지한다. - 자동·추가 선택 시트를 모두 실제 입력·썸네일·@목록·이미지 입력 한도에 반영한다. 삭제되거나 연결이 해제된 시트의 참조 오류는 계속 검사한다.
시점·시트 선택/버전·카드 설명/정의 버전을 해시한 검토 키를 promptVersion에 저장한다. 규칙 변경에 맞춰 storyboard-v3를 사용한다. 기존 프롬프트는 자동 변경하지 않으며 다시 제안받거나 내용을 검토한 뒤 저장해야 생성할 수 있다. 특히 예전 한 사람만, 다른 사람은 등장하지 않는다 문장은 새 제안으로 갱신하거나 직접 수정해야 한다. 수동 저장과 제안 완료 시 검토 키를 비교해 동시 변경을 감지한다.
검증: Backend 11개 suite/72개 테스트, 양쪽 TypeScript와 Frontend 범위 ESLint 통과. 시트 없는 인물·군중의 허용, 미선택 인물 설명 전달, 월드 시트와 추가 선택의 중복 제거 및 실제 생성 입력 순서, 기존 프롬프트 재검토를 확인했다. 유료 모델 생성은 실행하지 않았다.
2026-09-10 선택적 종료 프레임과 비트 간 연결
사용자 흐름
- 기존 카드는
시작 프레임으로 보존한다. 같은 카드의종료 프레임 · 선택탭에서 종료 프레임을 필요할 때만 추가한다. 추가 자체는 유료 생성 요청을 보내지 않는다. - 종료 프레임은 별도의 장면 설명·등장인물·프롬프트·생성 후보를 가진다. 시점은
END로 고정하며 해당 비트의 행동이 완료된 상태만 표현한다. 다음 비트의 행동을 미리 포함하지 않는다. - 종료 프레임을 준비할 때 시작 카드의 선택 이미지를 생성 기준으로 연결한다. 선택 이미지가 없다면 나중에 직접 연결해야 한다. 종료 생성에는 시작 기준 이미지가 필요하다.
- 변화가 없으면
시작 이미지 그대로 사용, 별도 파일이 있으면이미지 직접 지정을 사용한다. 직접 지정 해제 시 기존 생성 후보 선택으로 돌아갈 수 있다. 생성 후보를 새로 선택하면 직접 지정은 해제된다. - 다음 비트의 시작 탭에서
이전 이미지 이어서 만들기로 앞선 카드의 종료 이미지를 명시적으로 연결한다. 카메라 구도가 달라지는 독립 생성은 연결을 해제한다. - 종료 이미지가 없을 때 시작 이미지로 자동 대체하지 않는다. 사용자가 목록에서
시작을 직접 선택할 수 있다. 현재 연결 범위는 같은 씬의 앞선 비트에 속한 카드로 제한한다. - 보드 모아보기에서 시작·종료 이미지를 함께 확인한다. 이미지 확대와 기준 변경 안내를 제공한다.
저장과 생성 계약
- 마이그레이션
1789787800000-AddStoryboardEndFrames는cinema_shots에storyboardStartCardId,storyboardImageOverride,storyboardContinuity를 추가한다. 기존 이미지·프롬프트는 이동하거나 다시 생성하지 않는다. - 종료 프레임은 기존 생성 파이프라인을 재사용하는 별도 행이며 시작 카드와 같은 Scene에 속한다. 시작 카드당 종료 행 하나를 unique index로 보장하고 생성 API는 멱등 처리한다. 목록 API는 종료 행을
endCard로 묶어 별도 일반 카드로 노출하지 않는다. - 시작 카드 보관 시 종료 프레임도 편집·생성을 막는다. 종료의 카드 순서·보관은 시작 카드에서 관리한다.
- 연결과 시작 이미지 복사는 원본 경로·결과 ID·선택 이미지 key를 스냅샷으로 저장한다. 원본 선택이 변경·해제되거나 비트/카드가 보관되면 경고하고 재연결 전 생성을 차단한다. 이전 결과나 연결 당시 경로를 자동으로 덮어쓰지 않는다.
- 소유권·현재 프로젝트/Scene·활성 시나리오·카드 버전을 검증한다. 종료는 자신의 시작 카드에만, 시작은 앞선 비트의 카드에만 연결해 순환·미래 참조를 막는다. 연속성 참조 중인 생성 결과도 삭제 보호 대상이다.
- 실제 생성 입력은
연속성 기준을 첫 이미지로 전달하고, 필요하면 배치 가이드와 해당 시점의 인물 시트를 추가한다. 중복되는 일반 배경 입력은 제외한다. 가이드는 목표 배치·방향, 기준 이미지는 기존 구도·공간·조명·외형을 담당한다. - 프롬프트는 기준 이미지에서 필요한 변화만 반영하도록 하며, 새로 등장한 인물을 기존 얼굴로 복제하지 않도록 인물 시트는 해당 인물의 외형에만 사용하고, 등장 여부·추가 인물은 해당 프레임 설명으로 결정한다. END 전용 프롬프트 제안과 생성 시간 조건을 추가했다.
- 기존 시네마 이미지 워크플로우와 프로젝트 공통 비율·해상도를 사용한다. 생성 snapshot에 종료의 시작 카드 ID와 연속성 스냅샷을 기록한다.
API는 기존 storyboard 경로 아래에 추가한다.
| 메서드·경로 | 동작 |
|---|---|
| POST /:cardId/end-frame | 선택적인 종료 프레임 준비 |
| GET /:cardId/frame-links | 현재 이미지·연속성 기준·선택 가능한 앞선 이미지 조회 |
| PATCH /:cardId/frame-links | expectedVersion과 LINK/UNLINK/USE_START/UPLOAD/USE_GENERATED로 연결 또는 종료 이미지 지정 |
검증
- Backend 관련 70개 테스트, 양쪽 TypeScript, Frontend 변경 범위 ESLint를 확인했다.
- 로컬 DB 마이그레이션 적용을 확인했다. 실제 DB 트랜잭션에서 연결 저장·동시 변경 거부·기준 이미지 변경 감지·과거 경로 보존을 검증하고 해당 변경은 전부 롤백했다.
- 브라우저에서 기존 시작 카드 보존, 종료 준비·독립적인 등장인물, 시작 이미지 복사 저장, 다음 비트의 종료 이미지 후보 노출을 확인했다. 검증용 이미지 복사 선택은 해제하고 생성 준비 상태를 유지했다.
- 유료 프롬프트 제안·이미지 생성은 실행하지 않았다. 모델의 실제 연속성 품질과 자동 영상 마지막 프레임 추출은 별도 검증/후속 범위다. 현재 직접 지정은 정지 이미지 업로드다.
2026-09-10 이미지 중심 스토리보드 편집 개편
화면 구성과 사용 흐름
- 상단 가로 목록에 비트 순서대로 시작·종료 이미지와 상태를 표시한다. 한 비트에 카드가 여러 개면 가로로 이어 보여 목록 높이가 계속 늘어나지 않게 한다. 미작업, 생성 중, 최근 실패, 후보 선택 필요, 선택됨, 원본 확인 필요를 문구로 구분한다.
- 진행 수치는 활성 비트의 활성 시작 카드만 대상으로 한다. 선택하지 않은 종료 프레임과 보관 카드는 필수 미완료 작업으로 계산하지 않는다.
전체 보드는 같은 데이터를 큰 격자로 보여준다. 편집기를 제거하지 않고 숨겨 현재 카드·시작/종료 탭·수동 프롬프트 초안을 유지한다. 다른 카드로 이동할 때의 미저장 확인과 페이지 이탈 보호는 유지한다.- 편집 화면은 큰 이미지/후보와 장면 편집을 나란히 배치하고 좁은 화면에서는 세로로 배치한다. 후보 클릭은 미리보기만 바꾸며
이 이미지 사용으로 보드 선택을 저장한다. 새 생성 중에도 기존 선택을 보존한다. 기존 Shot 화면의 다중 역할 선택 계약은 변경하지 않는다. - 원본 Beat 대본은 기본 펼침 상태로 유지하며 긴 대본은 내부 스크롤로 읽는다. 제목·프레임 시점·첨부할 캐릭터 시트는 접힌 상세 설정에서 수정한다. 카드 순서·보관·복원은 카드 관리, 이전 Shot 설계·보관 이력은 더보기 메뉴에 둔다.
담을 장면이 기본 입력이다. 동일한 참조 목록을 공유해 메인 장면 입력과 상세 프롬프트 모두 @태그를 사용할 수 있다. 참조 영역은 +로 열고 닫으며 썸네일, 확대, 삭제, 프로젝트 에셋 선택, 이미지 업로드를 유지한다.- 상세 프롬프트는 기본적으로 접는다. 장면/시점/시트가 변경되면 검토 안내와 프롬프트 제안 버튼을 표시한다. 기존 수동 프롬프트를 자동으로 다시 쓰지 않으며 직접 검토 후 저장하거나 유료 제안을 선택한다. 이미지 생성은 별도의 명시적인 클릭으로 실행한다. 프롬프트 제안과 이미지 생성을 자동 연속 실행하지 않는다.
- 공통 비율·화질·FPS는 페이지 상단에 한 번 표시한다. 생성 버튼에는 현재 연결 워크플로우의 확정 화질별 1장 비용을 표시한다. 가격 조회 불가를 무료로 표시하지 않는다. 실제 차감은 기존 생성 서버의 검증과 가격 계산을 따른다.
- 연결된 이전 기준 이미지는 현재 이미지 아래에 항상 요약하고,
이전 기준과 현재 이미지 비교를 펼쳐 나란히 확인한다. 종료 프레임의 시작 이미지 복사·직접 지정·지정 해제와 이전 프레임 연결/해제 기능은 상세 영역에서 유지한다. 종료 이미지가 없을 때 시작 이미지로 자동 대체하지 않는다.
저장·호환성
장면 설명·제목·프레임 설정은 입력을 멈춘 뒤 900ms에 자동 저장한다. 저장 중에도 새로 입력한 장면 초안은 유지한다. 실패 시 오류와 다시 저장 버튼을 표시하고 반복 자동 요청을 중단한다. 편집 버전이 다른 경우 서버 변경을 덮어쓰지 않는다. 상세 프롬프트와 참조는 기존의 검토/저장 또는 생성 전 저장 흐름을 사용하며 저장되지 않은 상태를 이동 보호에 포함한다.
카드/배치 저장과 이미지 선택이 겹쳐 버전 충돌을 일으키지 않도록 관련 변경 버튼을 잠근다. 보관된 시작 카드는 편집/생성을 막되 활성 프로젝트에서 복원은 가능하도록 읽기 전용 조건을 분리했다. 종료 프레임은 보관된 시작 카드의 잠금을 따른다.
프롬프트 변경 API의 응답을 캐시에 반영할 때 기존 입력 한도·가격 등 조회 정보를 보존하고 다시 조회한다. 저장 직후 참조 입력 한도와 비용이 사라지는 현상을 방지한다.
저장 모델·기존 생성 결과·참조 매핑·월드 캡처/더미 배치·카메라 기준 몸 방향·시나리오 공통 제작 설정·시작/종료 시간 규칙은 유지한다. 새 마이그레이션이나 기존 결과 재생성이 필요하지 않다.
이미지 목록 조회
GET /v1/cinema/projects/:projectId/sequences/:sequenceId/scenes/:sceneId/storyboard/overview를 추가했다. 기존 storyboard 목록 서비스에서 소유자와 전체 경로 범위를 검증한 후 해당 카드들의 생성 결과를 한 번에 조회한다. 각 카드의 선택 이미지 URL, 후보 수, 생성 상태, 연결 원본 카드 ID를 반환한다. 각 카드마다 전체 연속성 후보 목록을 재조회하지 않는다. 생성 요청 직후 목록을 갱신하고 생성 중에는 5초 간격으로 확인한다.
적용 근거
이미지와 상태를 상시 노출하는 방식은 상태 가시성과 기억 부담 감소, 상세 설정을 접는 방식은 점진적 정보 공개, 이미지 선택과 생성 행동을 해당 대상 가까이 두는 방식은 대상과 행동의 근접 배치 원칙에 맞췄다. 이미지 후보와 보드 선택을 구분하고 원본 대본·연속성 기준을 확인할 수 있게 해 단순화로 인해 작업 맥락이 사라지지 않게 했다.
검증과 한계
- Backend 관련 13개 suite/80개 테스트, Frontend·Backend TypeScript, Frontend 변경 범위 ESLint, diff 공백 검사를 통과했다. 새 목록의 소유권 검증, 후보/선택/생성 중/실패/원본 누락/직접 지정 구분, 화질별 가격 조회를 추가 검증했다. 기존 프레임 생성·참조·월드 방향·시작/종료·시트 기본 선택 테스트를 함께 실행했다.
- 실제 localhost 페이지에서 기존 시작/종료 이미지 유지, 비트 이동, 전체 보드, 선택 후보/연결 기준 표시, 자동 저장 후 카드 이동·복원, @태그 키보드 삽입, 수동 프롬프트 초안 유지와 저장, 프로젝트 에셋 참조 추가·썸네일 삭제, 비용 표시, 보관·복원을 확인했다. 검증용 카드는 보관했고 기존 생성 이미지는 변경하지 않았다.
- 종료 이미지 복사·직접 업로드·연결·해제 버튼 접근과 나란히 비교를 확인했다. 이번 개편 검증에서 실제 파일 업로드나 월드 배치 저장, 유료 프롬프트 제안·이미지 생성은 다시 실행하지 않았다. 생성 요청 로직은 기존 회귀 테스트로 확인했으며 모델의 실제 생성 품질은 이번 UI 검증 범위에 포함하지 않는다.
- 1280×720 화면에서 편집/전체 보드 배치를 확인했고 새 검증 페이지의 브라우저 오류는 없었다. 모바일 실기기 검증은 별도다.
단계별 커밋: Backend 2eeec17(이미지 목록), e94dde6(생성 비용), Frontend d816fde1(비주얼 탐색·이미지 선택), f85d38af(장면 편집·초안 보호·연속성 비교).