Rive 애니메이션 마스터하기: React 개발자를 위한 완전 가이드

발행: (2025년 12월 17일 오후 01:32 GMT+9)
12 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have the text, I’ll translate it into Korean while preserving the original formatting and markdown.

소개

현대 웹 개발에서 활기차고 흥미로운 사용자 경험(UX)을 만들려면 단순한 CSS 전환만으로는 부족합니다. 앱을 느리게 만들지 않으면서도 멋진 복잡하고 인터랙티브한 애니메이션이 필요합니다. 그래서 Rive가 우리 기술 스택에서 강력한 “비밀 무기”가 된 것입니다.

오늘은 Rive가 무엇인지 이해하는 단계부터 아키텍처를 설계하고 실제 소스 코드를 사용해 구현하는 전체 과정을 살펴보겠습니다.

Part 1: Rive Basics – From Design to Code

1. What is Rive? And How Do We Create It?

Rive는 디자인 툴(Editor)런타임 엔진 을 포함하는 완전한 생태계입니다. JSON 파일만 재생하는 Lottie 나 비디오와는 달리, Rive는 실제 인터랙티브 머신처럼 동작합니다.

RiveFlow

The Workflow

Rive 작업은 Figma(그리기)와 After Effects(움직임)를 결합하고, Unity 게임처럼 약간의 로직을 더한 것과 같습니다.

PhaseWhat HappensTips
Design디자이너는 웹 또는 데스크톱에서 Rive Editor 를 사용합니다. Rive 안에서 직접 그리거나 Figma에서 아이템을 가져올 수 있습니다.레이어 이름을 명확히 지정하세요(예: UserAvatar, ScoreText). 개발자가 코드에서 쉽게 찾을 수 있습니다.
Animate디자이너는 타임라인을 이용해 움직임을 만듭니다(예: Run, Jump, Idle). Rive는 부드러운 캐릭터 움직임을 위해 “뼈대”(skeletal animation)를 사용합니다.
State Machine (Logic)디자이너는 애니메이션을 로직과 연결합니다. 예시: isRunning 입력이 true일 때 Idle에서 Running으로 전환합니다.입력 이름(Triggers, Numbers, Booleans)을 합의해 두면 React가 애니메이션을 완벽히 제어할 수 있습니다.

2. The Difference Between .REV and .RIV

ExtensionDescriptionUsage
.REV소스 프로젝트 파일(압축되지 않음). 포토샵의 .PSD와 같은 역할입니다. 향후 편집을 위해 안전하게 보관하세요. 앱에 포함하지 마세요.디자인 및 애니메이션 반복 작업.
.RIV런타임 파일(바이너리, 최적화, 작음). 웹에서 바로 실행할 수 있습니다. src/assets에 배치하는 파일입니다.프로덕션 번들.

3. Where to Find Free Rive Files?

디자이너가 없지만 연습하고 싶다면 Rive Community 를 확인하세요 – “애니메이션을 위한 GitHub”와 같습니다. “Loading”이나 “Button”을 검색하고 Remix를 클릭해 어떻게 만들어졌는지 살펴본 뒤, .riv 파일을 무료로 내보낼 수 있습니다.

Part 2: 우리 아키텍처의 기술 구현

1. 프로젝트 구조

현재 코드베이스를 기준으로, 파일들을 다음과 같이 구성합니다:

src/
├─ constants/
│   └─ rive.js               # 아트보드 이름, 상태 머신, 입력 이름 (오타 방지)
├─ utils/
│   └─ riveUtils.js          # 설정을 빠르게 만들기 위한 헬퍼 함수
├─ hooks/
│   └─ useRiveAnimation.js   # 시스템의 “핵심” – 로딩, 업데이트, 이벤트 처리
└─ view/
    └─ shared/.../Templates  # 훅을 사용하는 UI 컴포넌트(팝업, 배너, 위젯)

2. 심층 분석: useRiveAnimation

이 훅은 로딩 상태, 오류 처리, 그리고 가장 중요한 동적 콘텐츠 업데이트(애니메이션 내부의 텍스트나 이미지 변경)와 같은 어려운 문제들을 해결합니다.

integratewithhook

작동 방식

훅은 src, artboard, stateMachines를 포함한 설정 객체를 받습니다.

// src/hooks/useRiveAnimation.js (simplified)
const { rive, RiveComponent } = useRive({
  src: finalSrc,
  artboard,
  stateMachines,
  autoplay,
  // …other options
});

핵심 기능: 동적 업데이트

updateMultipleContents를 사용하면 실행 중인 애니메이션 파일 안의 텍스트(예: 카운트다운 타이머)나 이미지(예: 사용자 아바타)를 변경할 수 있습니다. 훅은 여러 RiveFieldType을 지원합니다:

RiveFieldType역할
String텍스트 내용 변경
Image애니메이션 내부 이미지 교체
Trigger / Boolean / Number애니메이션 로직/플로우 제어

구현은 Promise.allSettled를 사용해 업데이트를 배치 처리함으로써 최적의 성능을 제공합니다:

// Efficient batch updating
const updateMultipleContents = useCallback(
  async (updates, artboardPath) => {
    // …logic to locate the view model (vmi)…
    const promises = updates.map(({ path, type, value }) => {
      switch (type) {
        case RiveFieldType.String:
          return updateStringValue(vmi, path, value);
        case RiveFieldType.Image:
          return updateImageValue(vmi, path, value);
        // …handle other types…
      }
    });
    await Promise.allSettled(promises);
  },
  [rive]
);

3. 컴포넌트에서 훅 사용하기

import { useRiveAnimation } from '@/hooks/useRiveAnimation';
import { RIVE_ASSETS } from '@/constants/rive';

export const AvatarBadge = ({ avatarUrl, userName }) => {
  const { RiveComponent, setInput, updateMultipleContents } = useRiveAnimation({
    src: RIVE_ASSETS.avatarBadge,
    artboard: 'Main',
    stateMachines: ['BadgeSM'],
    autoplay: true,
  });

  // props가 변경될 때 아바타 이미지와 이름을 업데이트
  useEffect(() => {
    updateMultipleContents(
      [
        { path: 'Avatar/Image', type: RiveFieldType.Image, value: avatarUrl },
        { path: 'Name/Text', type: RiveFieldType.String, value: userName },
      ],
      'Main'
    );
  }, [avatarUrl, userName]);

  // 클릭 시 “pulse” 애니메이션을 트리거하는 예시
  const handleClick = () => setInput('PulseTrigger', true);

  return (
    <div onClick={handleClick}>
      <RiveComponent />
    </div>
  );
};

요약

  • Rive는 디자인 우선, 코드 준비된 애니메이션 워크플로우를 제공합니다.
  • 반복 작업을 위해 .rev 파일을 보관하고, 배포 시에는 .riv 파일만 사용합니다.
  • 중복을 방지하기 위해 상수, 유틸리티, 그리고 커스텀 useRiveAnimation 훅을 중앙화합니다.
  • updateMultipleContents를 사용해 텍스트, 이미지, 상태 머신 입력을 실시간으로 변경하여 UI를 반응형으로 유지하고 코드베이스를 관리하기 쉽게 합니다.

이 구성을 통해 디자이너는 풍부한 인터랙티브 경험을 만들 수 있고, 개발자는 전체 애플리케이션에 걸쳐 이를 깔끔하고 효율적으로 통합할 수 있습니다.

rt 3: 구현 방법 (단계별 가이드)

우리는 Countdown Widget( WidgetManager/Templates/RiveAnimationTemplate.jsx 와 유사)를 만든다고 가정해 보겠습니다.

가이드

Step 1: 상수 준비

디자이너가 Rive 파일에 만든 입력값들을 정의합니다.

// src/constants/rive.js
export const WIDGET_STATE_MACHINE = "Widget SM";

export const RiveInputs = {
  WidgetIn: "Widget In",   // Trigger: Play appear animation
  WidgetHover: "Hover",     // Boolean: Is mouse hovering?
};

export const RiveFields = {
  Time: "Timer/Time",      // Path to the text object in Rive
};

Step 2: 컴포넌트에서 Hook 사용

Hook을 연결합니다. 컴포넌트를 표시하기 전에 항상 isLoaded 를 확인하세요.

const WidgetRive = ({ endTime }) => {
  const {
    RiveComponent,
    isLoaded,
    updateMultipleContents,
    triggerStateMachineInput,
  } = useRiveAnimation({
    src: "assets/widget.riv",
    artboard: "Main",
    stateMachines: WIDGET_STATE_MACHINE,
    autoplay: false, // We will play it manually later
  });

  // Update the timer every second
  useEffect(() => {
    if (isLoaded && endTime) {
      const timeStr = calculateTimeLeft(endTime);
      // Send new text to Rive
      updateMultipleContents([
        new RivePropertyUpdate(
          RiveFields.Time,
          RiveFieldType.String,
          timeStr
        ),
      ]);
    }
  }, [isLoaded, endTime]);

  return (
    <>
      {!isLoaded && <Spinner />}
      {isLoaded && <RiveComponent />}
    </>
  );
};

Step 3: 상호작용 처리

BannerManager 에서 사용자 클릭 및 호버를 손쉽게 처리합니다.

const handleMouseEnter = () => {
  // Tell Rive the mouse is over the banner
  triggerStateMachineInput(
    WIDGET_STATE_MACHINE,
    RiveInputs.WidgetHover,
    true
  );
};

const handleMouseLeave = () => {
  triggerStateMachineInput(
    WIDGET_STATE_MACHINE,
    RiveInputs.WidgetHover,
    false
  );
};

Part 4: 모범 사례 및 팁

애니메이션을 부드럽게(60 FPS) 유지하고 버그 없이 만들려면, 소스 코드에서 제공하는 다음 팁을 따르세요.

뷰 모델 캐싱

우리의 훅은 인스턴스(vmiDataRef)를 캐시합니다. 이는 매우 중요합니다—캐시 없이 타이머를 매초 업데이트하면 Rive가 텍스트 객체를 분당 60번 검색해야 하며, 이는 지연을 초래합니다.

리소스 로드 가드

파일이 실제로 다운로드될 때까지 Rive 컴포넌트를 렌더링하지 마세요, 특히 팝업에서는 더욱 그렇습니다.

if (isError) {
  return <FallbackImage />;
}
return isLoaded ? <RiveComponent /> : <LoadingSpinner />;

“깜빡임” 방지

데이터가 로드되기 전에 Rive가 기본 텍스트(“Text”)를 잠깐 표시할 수 있습니다. 다음과 같이 해결하세요:

  1. 초기에는 autoplay: false 로 설정합니다.
  2. isLoaded 가 될 때까지 기다립니다.
  3. updateMultipleContents 를 호출해 데이터를 설정합니다.
  4. 그런 다음 play() 를 호출합니다.

오류 처리

항상 대체 방안을 마련하세요. .riv 파일이 로드에 실패하면(404) 훅 내부의 isError 변수가 true 로 바뀝니다. 이를 이용해 정적 이미지나 닫기 버튼을 표시해 사용자가 화면에 갇히지 않도록 하세요.

결론

Rive는 단순한 장식 도구 그 이상입니다. movement logicReact code와 분리하여 디자이너는 아름다운 애니메이션을 만들고 개발자는 데이터 처리에 집중할 수 있습니다.

useRiveAnimation 훅을 마스터하면 프리미엄하고 매우 부드러운 느낌의 위젯, 배너, 팝‑업을 만들 수 있습니다. 즐거운 코딩 되세요!

Back to Blog

관련 글

더 보기 »

Drupal: Canvas 탐색 (파트 2)

Components 탐색 이전 포스트에서 나는 새로운 module을 익혔고, 좋은 시작을 보였다. 이번 포스트에서는 component를 탐구할 것이다.