Motion Lore 구축기 — AWS + Vercel을 활용한 AI 발레 자막 서비스
요약
발레 영상의 안무를 내러티브 자막으로 변환하는 Motion Lore의 기술 스택과 아키텍처를 소개합니다. AWS와 Vercel을 기반으로 Groq와 Gemini를 활용한 2단계 LLM 파이프라인 및 DynamoDB를 이용한 무상태(Stateless) 백엔드 구축 과정을 다룹니다.
핵심 포인트
- Groq와 Gemini를 결합한 2단계 LLM 파이프라인으로 자막 품질 향상
- DynamoDB를 활용하여 비동기 작업 상태를 관리하는 무상태 백엔드 설계
- S3와 Gemini File API를 활용한 효율적인 비디오 데이터 처리
- AWS FastAPI와 Vercel을 조합한 확장 가능한 서비스 아키텍처
문제 (The Problem)
발레는 아름답습니다. 하지만 발레와 함께 성장하지 않았다면 전혀 읽어낼 수 없는 영역이기도 합니다.
한 바이럴 YouTube Short 영상이 이 문제에 대한 우리의 생각을 바꾸어 놓았습니다. 무용수의 움직임을 평이한 언어로 번역하여 직접 추가한 자막은, 발레에 전혀 관심이 없던 사람들을 갑자기 매료시켰습니다. 어떤 사용자는 자신이 자폐 스펙트럼이 있는데, 이 자막 덕분에 처음으로 발레가 이해되었다고 말하기도 했습니다.
우리는 이 과정을 자동화하고 싶었습니다. Motion Lore는 발레 영상을 위한 동기화된 내러티브 자막을 생성합니다. YouTube URL을 넣거나 파일을 업로드하면, 안무를 이야기처럼 읽어주는 자막 트랙을 돌려받을 수 있습니다.
AWS + Vercel 기반으로 백엔드를 어떻게 구축했는지 소개합니다.
아키텍처 (The Architecture)
2단계 LLM 파이프라인 (Two-Pass LLM Pipeline)
맥락(Context) 없이 원본 영상 푸티지만을 보는 단일 모델은 평범한 자막만을 생성합니다. 그래서 우리는 이를 두 단계로 나누었습니다:
- **Groq (llama-3.3-70b)**가 영상 제목을 읽고, 발레 작품을 식별하며, 내러티브 맥락(캐릭터, 줄거리, 배경 등)을 풍부하게 보완합니다.
- 해당 맥락은 Gemini 2.5 Flash가 영상을 분석하기 전에 시스템 프롬프트(System Prompt)에 주입됩니다. 이제 Gemini는 맹목적으로 추측하지 않습니다. 자신이 보고 있는 것이 무작위 댄스 클립이 아니라 '지젤(Giselle)'이라는 것을 알고 있습니다.
품질의 차이는 확연합니다.
DynamoDB — 두 개의 테이블, 두 개의 역할
ballet-subtitles 테이블은 영상 URL 또는 파일의 SHA-256 해시를 통해 콘텐츠 주소 지정(Content-addressed) 방식으로 관리됩니다. 수천 명의 서로 다른 사용자가 동일한 영상을 업로드하더라도, 영상은 정확히 한 번만 처리되어 영구적으로 캐싱됩니다. 불필요한 Gemini 호출을 제로(Zero)로 만듭니다.
ballet-jobs 테이블은 우리의 비동기 작업 상태 머신(Async job state machine)입니다 — queued(대기) → processing(처리 중) → done(완료) → failed(실패). 여기서 핵심적인 결정은 작업 상태를 초기부터 *메모리(Memory)*가 아닌 DynamoDB로 옮긴 것이었습니다. 인메모리(In-memory) 상태는 재시작 시 소멸하며 인스턴스 간 확장이 불가능합니다. DynamoDB를 사용함으로써 우리의 FastAPI 백엔드는 완전히 무상태(Stateless)가 되었습니다.
업로드를 위한 S3 (S3 for Uploads)
사용자가 업로드한 비디오는 S3에 임시로 저장됩니다. Gemini가 Gemini File API를 통해 이를 처리하고 나면 삭제됩니다. YouTube URL의 경우 S3를 완전히 건너뜁니다. Gemini에는 URL을 직접 처리할 수 있는 네이티브 URL 핸들러(Native URL handler)가 있어, 별도의 다운로드 파이프라인 전체를 절약할 수 있었습니다.
AWS 상의 FastAPI
무상태(Stateless), 수평적 확장 가능(Horizontally scalable), 서버 측 세션 없음. 모든 상태는 DynamoDB에 저장됩니다. 필요한 만큼 인스턴스를 얼마든지 생성할 수 있습니다.
프론트엔드를 위한 Vercel
React 프론트엔드는 Vercel에 배포되었습니다. 무상태인 FastAPI 백엔드 덕분에 이 조합은 매우 깔끔했습니다. Vercel은 에지 전달(Edge delivery)을 담당하고, 프론트엔드는 REST를 통해 AWS 백엔드와 직접 통신합니다. 마찰이 없습니다.
까다로웠던 부분들
Gemini + S3: Gemini는 S3에서 직접 데이터를 가져올 수 없습니다. 따라서 모든 업로드를 중간 단계로서 Gemini File API를 거치도록 경로를 지정해야 했습니다. 이는 문서만 봐서는 명확하지 않은 부분이었습니다.
확장 시 작업 상태(Job state) 관리: 초기 단계에서는 인메모리(In-memory) 작업 추적 방식을 사용했습니다. 로컬에서는 잘 작동했지만, 실제 부하가 걸리거나 재시작이 발생하자마자 즉시 문제가 생겼습니다. DynamoDB가 이 문제를 완전히 해결해 주었습니다.
컨텍스트 오염 (Context poisoning): 초기에는 단순한 파일 해시(File hash)가 식별자로서 Gemini에 그대로 전달되었습니다. 제목도, 컨텍스트도 없었습니다. 그 결과 자막이 실제 발레 내용과 무관하고 일반적인 내용으로 돌아왔습니다. Gemini 호출 전에 실행되는 Groq 분류 게이트(Classification gate)를 추가함으로써 품질 문제를 근본적으로 해결했습니다.
다르게 시도한다면
솔직히 말해서, 구축 중간에 마이그레이션하는 대신 첫날부터 DynamoDB 작업 상태 테이블을 추가했을 것입니다. 인메모리 방식은 갑자기 문제가 생기기 전까지는 괜찮게 느껴지기 마련입니다.
다음 단계
- 오페라 및 인도 고전 무용 양식 지원
- 향후 생성 품질을 개선하기 위한 커뮤니티 자막 평가 기능
- 발레단 웹사이트를 위한 임베디드 위젯
#H0Hackathon 을 위해 제작되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기