이 기사는 동영상 생성 서비스를 소개하기 위한 글이 아니라, 생성 작업(Job)을 웹 애플리케이션 측에서 어떻게 다룰지 고민하는 구현 메모입니다. 중심은 Next.js, Prisma, 작업 상태(Job State), 리뷰 저장 설계입니다. 생성 AI를 도입하는 화면에서는 API를 호출하는 처리보다, 입력, 상태, 실패, 확인 완료 기록을 어떻게 남길지가 나중에 중요해집니다.
데이터 모델
먼저, 생성 요청(Request)과 결과물(Artifact)을 동일한 테이블에 두지 않는 방침으로 합니다. 요청은 사람이 입력한 의도, 결과물은 외부 처리의 결과, 리뷰는 공개 판단입니다. 이 세 가지를 분리하면 재실행이나 반려(差し戻し)를 처리하기 쉬워집니다.
model VideoJob {
id String @id @default(cuid())
title String
...
Server Action의 경계
Next.js에서는 화면의 입력값을 직접 외부 API로 전달하는 것이 아니라, 일단 작업(Job)으로서 저장합니다. Server Action에서는 검증, 저장, 큐(Queue) 투입까지를 담당하게 합니다. 브라우저에는 외부 서비스의 상세 내용을 반환하지 않고, jobId만 반환하는 구성으로 합니다.
'use server'
import { z } from 'zod'
import { prisma } from '@/lib/prisma'
...
이렇게 구성해 두면 UI 측은 대기 시간이나 내부 에러에 휘둘리지 않습니다. 생성 처리가 느리거나 실패한 경우에도 화면은 작업의 상태를 읽기만 하면 됩니다. 작업 상태를 저장해 두면 페이지를 닫아도 작업이 사라지지 않으며, 리뷰 담당자도 나중에 확인할 수 있습니다.
목록 화면의 상태 설계
관리 화면에서는 READY만 표시하는 것이 아니라, QUEUED, RUNNING, FAILED도 보이도록 합니다. FAILED를 숨기면 재실행해야 할 입력값이 남지 않습니다. 상태별로 버튼을 나누면 운용이 안정됩니다.
export function JobActions({ status, jobId }: { status: string; jobId: string }) {
if (status === 'FAILED') return <button data-job={jobId}>Retry</button>
if (status === 'READY') return <button data-job={jobId}>Review</button>
...
리뷰는 코멘트만으로 하지 않는다
공개 판단을 자유 입력 코멘트로만 남기면 검색이나 집계가 어려워집니다. verdict를 필수 항목으로 하고, comment는 보충 사항으로 합니다. APPROVED, NEEDS_EDIT, REJECTED의 세 가지 선택지만 있어도 나중에 채택률이나 반려율을 확인할 수 있습니다. 나아가 meta에 종횡비(Aspect Ratio), 길이, 생성 시각, 참조 URL을 저장해 두면 동일한 입력으로 재생성했을 때의 비교도 가능합니다.
참조 도구를 한 번만 명시한다
사내 문서나 Zenn 기사에서는 사용한 도구에 대한 링크를 본문 중에 한 번만 명시해 두면 독자가 전제를 확인하기 쉬워집니다. 이 설계 메모에서는 동영상 생성 측의 참조처로 Seedance 2.0 mini를 둡니다. 구현 기사에서는 도구 이름을 반복해서 쓰는 것보다 코드와 상태 전이(State Transition)를 중심으로 쓰는 것이 가독성이 좋습니다.
요약
동영상 생성 기능을 앱에 넣을 때는 생성 처리보다 먼저 작업 관리(Job Management)를 설계해야 나중에 되돌리는 일이 적어집니다. VideoJob, VideoAsset, ReviewNote를 분리하고, Server Action으로 입력을 저장하며, 목록 화면에서 상태를 추적하고, 리뷰를 구조화하여 남깁니다. 이 토대가 있다면 외부 생성 도구를 교체하는 경우에도 화면과 운용 규칙을 크게 바꾸지 않고 해결할 수 있습니다.
Discussion