디자이너가 씬 묘사 한 줄을 입력하면 이미지 모델 다섯이 병렬로 후보를 뽑고, 고른 한 장에 영상 모델이 모션을 입히고, 한국어 음성까지 합성한다. 내가 만들어 운영 중인 사내 웹 도구 이야기다.
왜 시중의 생성형 AI 도구를 그냥 쓰면 안 됐는지부터.
같은 프롬프트라도 모델은 매번 캐릭터를 새로 해석한다. 눈 간격, 비율, 의상 디테일이 조금씩 달라져 시리즈물로 늘어놓으면 어색함이 바로 보인다.
일러스트 외주는 일관성은 지켜주지만 컷 하나에 며칠씩 걸린다. SNS 운영 속도를 따라가지 못한다.
"레퍼런스 이미지를 꼭 첨부하세요"라는 규칙은 언젠가 잊힌다. 사람이 지켜야 하는 규칙은 시스템이 대신 지키게 만들어야 한다.
디자이너 입장에서 한 번의 작업은 이렇게 흐른다.
"노을 진 바닷가에서 캐릭터가 모래성을 쌓는 장면". 전체 시나리오 맥락은 선택 입력.
경량 LLM이 한국어 한 줄을 영문 디테일 프롬프트로 확장. 사용자가 쓴 부분은 보존.
모든 요청에 캐릭터 레퍼런스 자동 첨부. 장면당 후보 5장이 그리드로 나열됨.
장면마다 마음에 드는 컷을 체크. 고른 컷이 영상의 시작 프레임이 된다.
모션 프롬프트도 LLM이 초안 작성. 완료되면 브라우저 알림.
영상·시드 이미지·레퍼런스가 폴더 구조로 정리된 zip 한 개.
이 흐름의 중심 화면이 장면 × 모델 결과 그리드다. 한 번의 생성으로 장면당 후보 5장이 나오고, 디자이너는 행마다 한 장씩만 고른다. 모델 고르기라는 기술 결정을 결과 고르기라는 디자인 결정으로 바꾸는 장치다.
장면 × 모델 그리드. 같은 장면을 5개 모델이 동시에 그리고, 행마다 ✓ 한 장이 영상의 시드가 된다.
"어느 모델이 제일 좋아요?"라는 질문에는 시스템 차원에서 답하지 않기로 했다. 이미지는 다섯을 다 굴리고 결과로 고른다. 영상과 음성은 용도별로 역할을 나눴다.
한글 글자·간판·디테일 묘사에 유리. 레퍼런스를 한 장만 받는 제약이 있어 캐릭터 기준 이미지가 그 한 장을 차지한다.
레퍼런스를 여러 장 받아(multi-image parts) 환경·소품 일관성에 유리. 세대·속도가 다른 4종을 함께 굴려 스타일 폭을 확보.
시드 이미지 + 다중 레퍼런스. 고품질 베이스라인.
시드 이미지만 받는 경제형. 단순 모션에 충분.
다중 레퍼런스·레퍼런스 영상·멀티샷 지원. 소품 일관성이 실사용에서 검증됨. UI에 "레퍼런스 지원" 배지로 차이를 노출.
image vs start_image).
잘못된 필드로 보내면 에러 없이 무시되고 text-to-video로 동작해서, 캐릭터 일관성이 깨진 영상이
멀쩡한 얼굴로 나온다. 그래서 모델 정의 객체에 필드명을 명시하는 매핑 테이블을 박아 뒀다.
조용히 틀리는 지점은 사람이 기억할 일이 아니라 코드가 막을 일이다.
대체재가 아니라 보완재로 셋을 함께 둔다. 같은 멘트라도 콘텐츠 톤에 따라 고른다.
자연스러운 한국어 내레이션의 기본기. 감정 강도 조절.
웃음·한숨·속삭임 같은 비언어 태그를 본문에 끼워 넣는 방식([laughs], [whispering]).
릴스용 과장 연기에 강함.
한국어 억양·감정 특화 국산 엔진. 고품질 엔진(Sona 2)과 빠른 경제형(Supertonic 3) 중 선택하고, 보이스마다 다른 스타일 목록(기쁨·부끄러움·놀람…)에서 연기 톤을 고른다. 1회 호출 글자 수 상한이 작아 짧은 릴스 멘트에 맞는다.
이 도구의 정체성에 해당하는 부분. 방향은 전부 같다. 사람이 잊어도 시스템이 지킨다.
캐릭터 기준 이미지 한 장이 모든 이미지·영상 요청에 서버에서 자동 첨부된다. 디자이너가 잊거나 생략할 수 있는 경로 자체가 없다. 일관성의 베이스라인이다.
캐릭터 외에 매 컷 똑같아야 하는 것들(의자, 소품, 공간)을 디자이너가 직접 첨부하는 슬롯. 이미지 5장, 영상 7장까지. PC에서 드래그&드롭으로 올리면 변환과 축소는 전부 브라우저가 처리하고 서버는 작은 파일만 받는다. 히스토리에서 마음에 든 결과물을 버튼 한 번으로 레퍼런스로 되돌릴 수도 있다.
전체 시나리오 텍스트는 이미지 생성과 프롬프트 다듬기에만 주입하고 영상 프롬프트에는 넣지 않는다. 영상 모델이 시나리오를 서사로 해석해서 장면에 없는 내용을 만들어 버린 적이 있어서다.
영상 모션 프롬프트에 ENVIRONMENT LOCK / OBJECT LOCK /
CHARACTER LOCK 블록을 명시해 움직여도 되는 것과 고정할 것을 구분한다.
"깜빡이지 마" 같은 부정문은 모델이 무시하는 경향이 있어 "차분하고 일정한" 식의 긍정 서술로 쓴다.
빠르고 싼 드래프트용 모델은 라인업에 두지 않는다. 초안 뽑는 도구와 완성품 만드는 도구를 한 화면에 섞으면 품질 기준이 흐려진다. 이 도구는 후자만 맡는다.
영상은 시드 이미지에서 가려진 부위를 모델이 임의로 채운다. 그래서 UI가 "가급적 전신·정면 컷을 고르세요"라고 그 자리에서 알려 준다. 규칙은 문서가 아니라 화면에 산다.
디자이너는 한국어로 짧게 쓰고 싶어 하고, 이미지·영상 모델은 영문 디테일 프롬프트에서 잘 동작한다. 그 사이를 경량 LLM(Claude Haiku) 한 층이 메운다. 시스템 프롬프트는 프롬프트 캐싱으로 호출 비용을 줄인다.
"비 내리는 카페 창가에서 책 읽는 캐릭터" 한 줄을 배경, 조명, 감정, 구도가 들어간 영문 프롬프트로 늘린다. 사용자가 이미 쓴 내용은 보존하고 빈 부분만 보강한다. 도구가 사람의 의도를 덮어쓰지 않게 하기 위해서다. 캐릭터 외형 묘사는 건드리지 않는다. 외형은 레퍼런스 이미지 담당이라서.
영상용 모션 프롬프트는 한국어와 영어를 한 번에 생성한다. 한국어는 사람이 검토하고 수정하는 화면에, 영어는 영상 모델로 보낸다. 한국어를 고치면 영문에 낡았다는 표시가 뜨고 재번역 버튼으로 갱신한다. 검토 언어와 실행 언어를 분리하되 어긋남이 눈에 보이게 한 것.
이벤트 로그에 scene_original(사용자 입력 원문)과 scene_refined(LLM이 다듬은 결과)를
나란히 기록한다. 다듬기 품질이 좋은지, 어떤 입력에서 망가지는지를 나중에 실데이터로 확인할 수 있다.
가공 단계가 있는 파이프라인은 가공 전후를 함께 남겨야 가공기를 의심할 수 있다.
화려하지 않지만 이게 없으면 운영이 안 되는 부분. 스택은 표준적인 조합이다. Next.js 풀스택(서버 라우트가 AI 호출 프록시), Clerk 인증, Neon Postgres(사용자), Upstash Redis(쿼터), Google Drive·Sheets(생성물·로그), Vercel 호스팅, Sentry·Slack 관측.
회사 메일 도메인만 가입 가능(이메일 OTP). 외부 메일은 입구에서 차단.
첫 로그인은 "승인 대기" 상태로 시작하고 관리자가 허가해야 사용 가능. 화이트리스트를 통과해도 자동 입장은 없다.
다른 사내 도구들과 같은 인증 인스턴스를 공유한다. 한 번 로그인하면 사내 도구 전체에 통한다.
전역 키-값 카운터로 강제하고, 잔량이 20%에 닿으면 메신저로 한 번 알린다(사용자당 하루 한 번, 스팸 방지). 관리자 화면에서 사용자별 개별 한도 조정도 된다. 이미지 한도 기준으로 보면 이렇다.
쿼터와 별개로 제공사 쪽에도 월 예산 하드 캡을 걸어 둔다. 앱의 쿼터 로직에 버그가 나도 청구서는 캡에서 멈춘다. 안전장치는 서로 다른 층에 이중으로 둔다.
모든 이벤트(이미지·영상·음성 생성, 다듬기, 로그인, 레퍼런스 업로드의 6종)를 18컬럼 고정 스키마로 시트에 추가만 한다(append-only). 누가 언제 어떤 프롬프트로 어떤 모델을 써서 성공했는지가 한 줄씩 남고, 관리자 대시보드의 사용량 통계와 비용 추정, 모델별 채택 분포가 전부 이 시트에서 나온다. 별도 분석 인프라 없이 시트 하나로 감사 추적과 분석을 겸한다. 사내 도구 규모에는 이 정도가 맞다.
운영자가 매일 들여다보지 않아도 되도록, 닳는 자원과 쌓이는 자원에 각각 자동 장치를 붙였다.
업로드된 레퍼런스 이미지는 30일이 지나면 매일 새벽 예약 작업이 지운다. 지우기 전에 대상 폴더가 정말 레퍼런스 루트인지 런타임 검증을 거친다. 설정 오류 한 줄이 영구 삭제 사고가 되는 경로를 막기 위해서다. 정상 삭제는 조용히, 실패만 알림을 보낸다.
선불 크레딧제 TTS는 소진되는 순간부터 조용한 장애다. 예약 작업이 잔여 크레딧을 주기적으로 점검해 소진되기 전에 경고를 보낸다. 고장 나면 알림이 아니라, 고장 나기 전에 알림.
API 키 만료(401)나 크레딧 소진(402)은 사용자가 해결할 수 없는 문제다. 이런 에러는 사용자에게 일반 오류를 보여주는 대신 관리자 채널로 보낸다. 에러마다 이걸 고칠 수 있는 사람에게 가야 한다.
모든 외부 AI 호출은 재시도 래퍼로 감싼다. 과부하(429·529), 서버 오류(5xx), 네트워크 일시 장애는 지터를 둔 재시도로 흡수하고, 병렬 호출이 폭증하지 않게 시도 횟수는 명시적으로 제한한다.
캐릭터 도구가 아니어도 옮겨 심을 수 있는 것들.
"레퍼런스를 꼭 첨부하세요"는 언젠가 잊힌다. 자동 주입으로 바꾸면 잊는 경로 자체가 사라진다. 사람의 주의력에 기대는 규칙은 전부 자동화 후보다.
최고의 모델 논쟁은 답이 자주 바뀐다. 후보를 동시에 뽑아 사람이 고르게 하면 기술 결정이 사용자의 안목 결정으로 바뀌고, 모델 교체도 라인업 조정 문제가 된다.
필드명이 달라도 에러가 안 나는 API가 가장 위험한 부류다. 차이를 코드의 명시적 테이블로 강제하면 아는 사람만 아는 함정이 사라진다.
LLM이 끼어드는 파이프라인은 입력 원문과 가공 결과를 나란히 남겨야 나중에 가공기 자체를 의심하고 검증할 수 있다.
사용자가 해결 못 하는 에러는 관리자에게 보내고, 소진형 자원은 소진 전에 경고한다. 정상 동작은 조용히. 알림의 가치는 희소성에서 나온다.