
입 모양 AI 아바타를 3번이나 다시 만들고, 결국 '마스크로 입을 가리는 것'으로 정착한 이야기
요약
AI 뉴스 프로그램 파이프라인 고도화를 위해 아바타의 움직임을 구현하는 세 가지 방식을 시도한 과정과 결론을 다룹니다. Remotion 애니메이션과 Google Veo의 영상 생성 방식을 거쳐, 결국 입 모양 구현의 한계를 마스크로 가리는 방식으로 해결한 실전 경험을 공유합니다.
핵심 포인트
- Remotion을 이용한 파츠별 보간 애니메이션은 2D 이미지 변형의 부자연스러움으로 인해 기각됨
- Google Veo의 Image-to-Video 기능은 높은 품질을 보여주나 음성과 입 모양의 불일치 문제가 발생함
- 기술적 완성도보다 사용자 경험을 우선하여 '입을 마스크로 가리는' 발상의 전환으로 최종 정착함
서론
지난 기사에서는 매일 아침 전자동으로 AI 뉴스 프로그램을 만드는 파이프라인(yabou02)의 TTS를 클라우드로 이관한 이야기를 썼습니다.
그 후, "더 높은 퀄리티를 올리고 싶다"라는 욕심이 생겨 차세대 파이프라인 yabou03 개발을 시작했습니다. 이 기사는 그중에서도 가장 미궁에 빠졌던 아바타의 움직임에 관한 이야기입니다.
결론부터 말하자면, 세 가지 방식을 시도해 두 가지를 기각하고, **"입 모양(Lip-sync)을 열심히 만드는 것을 포기하고, 입 자체를 가린다"**라는 발상의 전환으로 결론을 내렸습니다. 오늘 마침 yabou03을 실전용으로 승격시켰기에 그 기록을 남깁니다.
Before: 정지화상 종이인형 방식
지금까지의 yabou02는 캐릭터(아크로파파)의 포즈 차분 이미지를 음량에 맞춰 전환하는 "종이인형" 방식이었습니다. 움직임은 뚝뚝 끊기고, 입 모양도 "열림/닫힘"의 2진법적인 움직임이었습니다. 나쁘지는 않았지만, "더 생동감 있게 만들고 싶다"라는 다음 목표를 위해서는 한 단계 더 높은 수준이 필요했습니다.
도전 1: Remotion 퍼펫(React 보간 애니메이션) → 기각
먼저 시도한 것은 캐릭터를 파츠별로 분해하여 Remotion(React로 프로그래밍하여 영상을 만드는 도구)의 spring() 보간법으로 움직이는 방식입니다. 몸, 머리, 입, 눈을 레이어로 나누고 호흡, 눈 깜빡임, 고개 기울임 등을 모두 수식으로 부드럽게 움직였습니다.
기술적으로는 흥미로운 구현이었습니다. 머리 레이어를 페더 마스크(Feather Mask)를 사용하여 약간 크게 복제함으로써, 아래에 있는 몸 쪽의 머리를 완전히 가릴 수 있는(=이미지의 "빈 공간 채우기" 처리가 불필요해지는) 발견도 있었습니다.
하지만 실제로 렌더링하여 본편에 합성해 본 사용자의 반응은 한마디였습니다.
"전혀 별로다"
2D 이미지의 변형 애니메이션은 아무래도 "그림이 늘어나고 줄어드는" 느낌을 지울 수 없어 부자연스러움이 앞섰습니다. 파츠 분해 및 보간이라는 설계 자체는 틀리지 않았다고 생각하지만, 정지화상 베이스인 이상 움직임의 품질에는 한계가 있다는 것을 통감했습니다.
도전 2: Veo(AI 영상 생성)를 통한 모션 루프 → 아쉬운 지점까지 도달
발상을 바꾸어, 이미지 생성 AI 자체에게 움직이게 하기로 했습니다. Google의 Veo 3.1에는 image-to-video 기능이 있어, 정지화상 1장과 프롬프트를 전달하면 해당 캐릭터가 움직이는 영상을 생성해 줍니다.
op = client.models.generate_videos(
model="veo-3.1-fast-generate-preview",
prompt="The man talks naturally and energetically to the camera...",
...
이것은 상상 이상의 품질이었습니다. 표정 풍부하게 말하고, 자연스럽게 눈을 깜빡이며, 8초 동안 캐릭터의 일관성(안대, 헤어스타일, 의상)도 거의 무너지지 않습니다. 녹색 배경(Green Screen)으로 생성하여 크로마키(Chroma Key) 작업을 하면 본편에 합성할 수 있습니다.
사용자의 반응도 좋았습니다.
"차원이 다르다! 이것만 성공하면 끝이다!"
그래서 "대기", "말하기(2종)", "끄덕임", "놀람", "인사"의 6가지 상태를 녹색 배경으로 일괄 생성하고, 나레이션 음량으로부터 발화 구간을 판정하여 상태를 자동으로 선택·연결하는 메커니즘을 만들었습니다. 한 번 생성하면 계속 재사용할 수 있으므로 매일 발생하는 추가 비용은 제로입니다.
두 가지 걸림돌
여기서도 두 가지 벽에 부딪혔습니다.
① 입의 움직임과 나레이션 음성이 맞지 않음
이것은 처음부터 알고 있었던 트레이드오프(Trade-off)였습니다. Veo가 생성하는 입의 움직임은 "그럴듯하게 말하는 듯한 모습"일 뿐, 실제 나레이션 음성과는 무관합니다. 사용자의 감상은 이러했습니다.
"퀄리티는 상당한데, 입 모양이 전혀 맞지 않으니 기분이 이상하다"
인간은 소리와 입의 동기화에 극도로 민감하기 때문에, 움직임의 품질이 높을수록 오히려 어긋남이 눈에 띄는 딜레마가 발생합니다.
② 클립의 연결 부위가 "페이지 넘기기"처럼 보임
8초짜리 루프 클립을 단순하게 하드 컷(Hard Cut)으로 연결하면, 클립의 끝에서 다음 클립의 시작으로 점프하는 순간 부자연스러운 "넘기기" 같은 움직임이 발생했습니다.
처음에는 클립 전체를 "순방향 재생 $\rightarrow$ 역방향 재생"의 팔린드롬(Palindrome, 왕복 방식)으로 만들어 반드시 시작 프레임으로 돌아온 뒤 전환하는 대책을 세웠지만, 그래도 미묘한 위화감이 남았습니다. 원인을 찾아보니, Veo가 생성한 클립의 종반부에 애초에 부자연스러운 움직임이 포함되어 있다는 것을 알게 되었습니다. 대책은 간단했습니다. 클립의 앞부분 4초만 사용하여 그 부분만 왕복시키는 방식으로 변경했습니다. 불필요한 꼬리 부분을 통째로 잘라냄으로써 해결했습니다.
앞부분 4초만 사용하여 정→역의 8초 루프 만들기
"[0:v]trim=duration=4,setpts=PTS-STARTPTS,split[a][b];"
"[b]reverse[r];[a][r]concat=n=2:v=1:a=0[v]"
연결 부위는 해결했지만, ①번의 입 모양 불일치(Lip-sync mismatch) 문제는 그대로 남아 있었습니다.
도전 3: 립싱크(Lip-sync) AI 검토 → 채택하지 않음
"그럼 입 모양만 나중에 음성에 맞춰서 다시 그리면 되지 않을까?"라는 생각에, 음성 구동 립싱크 AI (LatentSync나 MuseTalk 등) 도입을 검토했습니다. fal.ai나 Replicate 같은 호스트 실행 서비스(Hosted execution service)를 통해 API 한 번으로 테스트해 볼 수 있습니다.
비용을 산출해 본 결과, PoC(Proof of Concept) 자체는 수십 엔 정도로 끝날 수 있지만, 매일 16분의 영상을 호스트 API로 흘려보내면 월 2만 엔이 넘는다는 것을 알게 되었습니다 (오픈 소스 모델을 자체 GPU에 호스팅하면 저렴해지겠지만, 작업 규모가 한 단계 커집니다). '입 모양을 맞춘다'는 정공법은 기술적으로는 타당하지만, 비용과 공수 양면에서 이번에는 보류했습니다.
발상의 전환: "입 모양에 대한 기대치를 없애기"
여기서 한 번 멈춰서 다시 생각했습니다. 입 모양을 맞추려고 하니까 '맞지 않는다'가 문제가 되는 것이다. 그렇다면, 입이 보이지 않으면 되지 않을까?
구체적으로는, 캐릭터에게 사이버펑크 스타일의 보이스 모듈레이터형 마스크를 착용시켜 입을 완전히 가려버렸습니다. 그리고 마스크에 달린 LED 라인(보이스 인디케이터)을 나레이션의 음량에 맞춰 빛나게 하기로 했습니다.
음성 음량 엔벨로프 (Envelope) ──→ 마스크 LED의 발광 강도
이 방식의 강점은 동기화 어긋남(Sync drift)이 구조적으로 발생할 수 없다는 점입니다. 입 모양을 추측할 필요 없이, 단순히 음량에서 발광 강도로 매핑(Mapping)하는 것이기에 100% 정확하게 소리와 연동됩니다. 구현 또한 외부 API를 사용하지 않고 자체적인 numpy 처리만으로 완결되며, 추가 비용은 제로입니다.
gain = 0.12 + 0.88 * float(level) # 무음일 때도 미세하게 점등, 음량에 따라 플래시
frame[..., 3] = (alpha_base * gain).astype(np.uint8)
캐릭터 디자인 측면에서도 네온 사이버펑크 프로그램의 세계관에 마스크가 자연스럽게 어우러져, 오히려 비주얼의 완성도가 높아졌습니다. 사용자 반응도 단번에 "OK"였습니다.
"아~! 마스크 안은 정말 좋다! 시도해 보고 싶어!"
Veo의 루프 생성은 '마스크 버전 아크로파파'로 다시 만들었고, 입 모양 문제는 사라진 채 Veo의 풍부한 몸짓, 눈 깜빡임, 고개 끄덕임 같은 연기는 그대로 유지할 수 있었습니다.
그 외 이번 업데이트 내용
아바타 외에도 이 기간 동안 몇 가지 개선을 진행했습니다.
목소리를 낮게 깔고 1.3배속으로
TTS(Text-to-Speech) 목소리를 여러 저음 보이스 후보를 비교해 본 끝에, "낮고 중후하며 관록 있는 베테랑 뉴스 캐스터"라는 스타일 지시로 변경하고, 재생 속도를 1.3배(피치 유지)로 높였습니다. 템포가 빨라지면서 영상 자체도 짧아져(16분 → 약 12분 반), 시청 경험의 속도감이 좋아졌습니다.
# 청크(Chunk)마다 atempo(피치 유지)를 적용한 후 길이를 실측함
# → 자막, 슬라이드, 아바타의 동기화는 자동으로 추종함
subprocess.run(["ffmpeg", "-i", wav, "-filter:a", f"atempo={speed}", out])
속도 변환을 "실측한 후 동기화를 구성한다"는 기존 설계 덕분에, 이 변경 사항은 다른 동기화 처리에 전혀 손을 대지 않고도 완료할 수 있었습니다.
뉴스의 신선도 문제 수정
"6월 뉴스가 7월에 소개되는" 신선도 문제도 발견되었습니다. 원인은 뉴스 수집에 사용하던 Brave Search의 웹 검색 엔드포인트가 '최근 업데이트된 페이지'를 반환하는 사양이었기 때문인데, 매주 업데이트되는 SEO 정리 기사(Best AI Tools 2026 등)가 오래된 화제를 끊임없이 재방송하고 있었던 것이었습니다.
대책으로 보도 기사에 특화된 News 엔드포인트로 전환하고, 게시일 필터(7일 이내), 정리 기사 타이틀의 기계적 제외를 구현했습니다. 이와 함께 대본 생성 프롬프트에도 "분량이 줄더라도 좋으니 신선함을 우선할 것", "메이저한 기보도보다 매니악한 1차 정보를 우선할 것"이라는 규칙을 추가했습니다.
Remotion을 이용한 모션 그래픽스
두 단계 전 기사에서 소개했던 도표 슬라이드(막대그래프, 수치 카운트업, 플로우차트 등)도 Pillow의 정지 영상에서 Remotion의 애니메이션으로 교체했습니다. 구성 작가 AI가 출력하는 JSON 형식은 그대로 유지하면서, 렌더링 엔진(drawing engine)만 교체하는 설계로 만들었기 때문에 기존 로직에 미치는 영향은 최소한으로 억제되었습니다.
비교: yabou02 와 yabou03
동일한 대본과 동일한 뉴스로부터 구형 파이프라인(yabou02)과 신형 파이프라인(yabou03)으로 각각 영상을 제작하여 나란히 비교해 보았습니다. 정지 영상+종이인형 아바타와 Remotion의 애니메이션 도표+마스크 아바타의 차이를 한눈에 확인할 수 있습니다(왼쪽이 구형, 오른쪽이 신형, 음성은 신형 보이스).
요약
- 2D 보간(Puppet)은 "움직임 품질의 천장이 낮기" 때문에 기각
- AI 영상 생성(Veo)은 움직임의 질은 압도적이지만, "음성과의 동기화"라는 또 다른 벽에 부딪힘
- 동기를 포기할 수 없는 부분은, 애초에 동기가 문제가 되지 않는 디자인으로 바꾸는 것(입을 가리고 LED를 빛나게 함)이 최종적인 해답
- 립싱크(Lip-sync) AI를 이용한 후처리는 기술적으로 타당하지만, 이번에는 비용 문제로 보류
- 목소리·속도·뉴스의 신선도·도표 애니메이션 등 세부적인 개선이 쌓여 전체적인 퀄리티가 향상됨
"정면으로 해결하는 것"만이 정답이 아니라, 문제의 정의 자체를 바꿈으로써 막혀 있던 벽을 넘을 수 있었다는 것이 이번의 가장 큰 배움입니다. yabou03는 오늘부터 실전 운영으로 승격되었습니다. 다음은 퍼펫(Puppet) 방식의 움직임에 재도전할지, 아니면 완전히 다른 방향으로 갈지——계속해서 기록해 나가겠습니다.
(이 기사에서 소개한 시스템이 매일 아침 제작하고 있는 영상은, YouTube의 아크로파파 AI 테크 뉴스(@acropapa330)에서 공개하고 있습니다)
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기