다른 챗봇을 만들지 마세요: Rive와 함께 'Duolingo-Style' AI Companion 설계

발행: (2025년 12월 24일 오후 09:10 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

우리는 “AI 래퍼”에 빠져 허우적거리고 있습니다. AI 언어 튜터, 롤플레이 앱, 혹은 정신 건강 동반자를 만들고 있다면, 한 가지 문제가 있습니다: 텍스트 인터페이스는 지루하다는 것.
현재 레이스에서 승리하고 있는 앱들(예: Duolingo의 Lily 혹은 character.ai)은 단순히 토큰을 출력하는 것이 아니라, 퍼포먼스를 렌더링하고 있습니다.

AI 인터랙션을 전문으로 하는 Rive 애니메이터로서, 나는 이 프로젝트들의 백엔드를 많이 보았습니다. “장난감” 앱과 “제품” 사이의 차이는 보통 한 가지에 달합니다: 립싱크 아키텍처.

이 글에서는 단순한 볼륨 바운싱을 넘어 음소‑정확한 스피치를 구현하기 위해 Rive를 사용해 반응형 립싱크 AI 캐릭터를 만드는 기술적 설정을 풀어보겠습니다.

아키텍처: 퍼펫 vs. 퍼페티어

생동감 있는 캐릭터를 만들려면 다음과 같이 관심사를 분리하세요:

  • 퍼펫 (Rive) – 숫자 입력에 따라 형태를 변형시키는 상태 머신.
  • 퍼페티어 (당신의 코드) – 오디오를 파싱하고 퍼펫에 신호를 보내는 React/Flutter/Swift 로직.

레벨 1: “뮤펫” 방식 (진폭)

빠른 방법. 내일 MVP가 필요하다면 여기서 시작하세요. 오디오 진폭의 RMS(Root Mean Square)를 분석합니다.

Rive 설정: 1‑D 블렌드 상태. 입력 0 = 입 닫힘, 입력 100 = 입 크게 열림.

// Example (pseudo‑code)
riveInput.value = normalizedVolume;

문제: 마치 뮤펫처럼 보입니다. 캐릭터가 “OO”와 “EE” 소리를 동일하게 입을 크게 열어, 뉘앙스가 부족합니다.

레벨 2: “비세메” 방식 (음소 매핑)

Duolingo 방식. 볼륨 사용을 중단하고 비세메(음소의 시각적 대응)를 사용합니다. 많은 TTS 제공업체(Azure Speech SDK, AWS Polly)는 비세메 이벤트—특정 타임스탬프에서 입 모양을 설명하는 정수—를 반환합니다.

Rive 상태 머신

단일 “입 열림” 블렌드 대신, ~12‑15개의 개별 입 모양을 가진 상태 머신을 구축합니다. 예시:

비세메설명
Sil침묵 / 대기
PP입술을 눌러서 – P, B, M
FF이가 입술에 닿음 – F, V
TH혀가 튀어나옴 – TH
DD혀가 이 뒤에 위치 – T, D, S
kk뒤쪽이 열림 – K, G
aa넓게 열림 – A
O둥글게 – O
(계속)

이들을 viseme_id라는 Number Input에 매핑합니다.

코드 로직

프론트엔드(React Native, Flutter 등)에서 비세메 이벤트를 수신하고 Rive에 전달합니다:

ttsService.on('visemeReceived', (visemeID) => {
  // 1. Get the Rive input
  const mouthInput = riveArtboard.findInput('viseme_id');

  // 2. Map the TTS provider's ID to your Rive ID
  // (Azure has 21 shapes, Rive might only need 12)
  const mappedID = mapAzureToRive(visemeID);

  // 3. Update the state
  mouthInput.value = mappedID;
});

비밀: 레이어드 마이크로‑행동

립싱크는 환상의 약 50 %에 불과합니다. 캐릭터가 말하는 동안 눈을 깜빡이지 않으면 불쾌감을 줍니다.

해결책: Rive에서 레이어드 상태 머신을 사용해 여러 타임라인이 충돌 없이 동시에 재생되도록 합니다.

  • 레이어 1 – 입 (코드가 제어).
  • 레이어 2 – 눈 (자체 루프). Rive 내부의 “Randomize” 리스너가 2–5 초마다 자동으로 눈 깜빡임이나 눈동자 움직임을 트리거합니다.
  • 레이어 3 – 감정 (isBored, isHappy, isThinking 같은 Boolean 입력).

“멈춤” 처리 (지연)

AI 음성 채팅에서 가장 큰 UX 적은는 LLM이 답변을 생성하는 동안 발생하는 2–3 초의 침묵입니다. 캐릭터가 멈추면 안 됩니다.

  1. 사용자가 말을 멈춤 → 앱이 isThinking = true로 설정.
  2. Rive 애니메이션 – 캐릭터가 고개를 들어 올리거나, 손가락을 두드리거나, (비꼬는 성격이라면) 눈을 굴립니다.
  3. 오디오 스트림 시작isThinking = false로 설정; 비세메 데이터 흐름이 재개됩니다.
Back to Blog

관련 글

더 보기 »

사이드 프로젝트와 AI

Side Projects와 AI를 위한 커버 이미지 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s...