Remotion 템플릿, 디자이너를 위해 GSAP으로 갈아엎은 이야기
– ArtXTech
Remotion 템플릿, GSAP으로 갈아엎은 이야기
Remotion, 이거 참… “텍스트만 좀 바꾸자, “애니메이션 타이밍 0.5초만 당겨달라” 같은 요청이 올 때마다 내가 직접 코드를 까서 수정해야 하는 이런 방식은 너무 시간이 오래 걸렸다.
이럴 바엔 걍 내가 애프터 이펙트를 익히는 게 나으려나 싶기도 했다.
목표는 명확했다. “반복된, 그러니까 패턴화가 된 영상 모션에는 코드 한 줄 안 건드리고, 설정 파일 하나만 슥슥 고쳐서 원하는 비디오를 뽑아낼 수 있게 만들자.” 이 한 문장을 목표로 지난 몇 주간 벌인 디버깅(이라 쓰고 아무도 요청하지 않는 개삽질이라 읽는다) 기록을 여기에 남긴다.
처음엔 KNKVideo 프로젝트의 간단한 Editor 컴포넌트를 손보는 것부터 시작했다. 근데 시작부터 props 타입이 개판이고, 컴포넌트끼리 데이터를 어떻게 주고받는지 한눈에 들어오지도 않았다. Remotion이 기본적으로 제공하는 템플릿 구조가 파일도 너무 잘게 쪼개놓고, 애니메이션 로직도 interpolate나 useCurrentFrame 같은 걸로 복잡하게 얽혀있으니, 이건 뭐 내가 만들어 놓고도(ㅋㅋㅋㅋㅅㅂ) 유지보수하기가 빡셌다.
코드 누가 짰는지 참 드럽게 짰넼ㅋㅋㅋ…아 맞다, 내가 짰지…ㅅㅂ ㅠㅠㅠ
특히 애니메이션. styled-components랑 Remotion의 interpolate 함수를 섞어서 애니메이션을 만드니 코드가 그냥 지옥이 되더라.
// 예시: 기존의 복잡한 애니메이션 코드
const titleStyle: React.CSSProperties = {
opacity: interpolate(frame, [0, 20], [0, 1]),
transform: `translateY(${interpolate(frame, [0, 20], [50, 0], {
extrapolateRight: 'clamp',
})}px)`,
};
const subtitleStyle: React.CSSProperties = {
opacity: interpolate(frame, [30, 50], [0, 1]),
// ... 이런 식으로 애니메이션이 컴포넌트마다 흩어져 있었음
};
OldAnimation.tsx
이런 코드가 컴포넌트마다 덕지덕지 붙어있다고 생각해 봐라. 애니메이션 순서 하나 바꾸려면 관련 파일을 다 뒤져야 했다. 간단한 쉐이프/컬러 변경 때마다 이걸 보고 수정하는 건 불가능에 가까웠지. 이건 좀 아니지예.
그래서 다 갈아엎기로 했다. 핵심은 두 가지였다.
- 복잡한 애니메이션 시스템 → 직관적인 GSAP 타임라인으로 교체
- 하드코딩된 컴포넌트 → 데이터 중심 설계로 전환
Remotion의 프레임 기반 애니메이션 대신, 그냥 직관적인 타임라인 하나 쫙 펼쳐놓고 “이때 이거 움직여!” 하면 끝나는 GSAP이 그리워졌다. 그래서 useGsapTimeline이라는 커스텀 훅을 만들어서 모든 애니메이션을 하나의 타임라인에서 관리하도록 바꿨다.
// 모든 애니메이션을 하나의 타임라인으로 통합
useGsapTimeline(() => {
const tl = gsap.timeline();
tl.from(titleRef.current, { opacity: 0, y: 50, duration: 0.5 })
.from(subtitleRef.current, { opacity: 0, duration: 0.5 }, "-=0.2")
.to(logoRef.current, { scale: 1.2, repeat: -1, yoyo: true });
// ... 이런 식으로 모든 애니메이션을 순차적으로 관리
return tl;
});
useGsapTimeline.tsx
코드가 훨씬 선언적이고 이해하기 쉬워졌다. 이제 애니메이션 순서나 타이밍을 바꾸는 건 식은 죽 먹기다.
두 번째, 그리고 가장 중요했던 건 데이터 중심 접근법이다. 모든 컨텐츠, 스타일, 심지어 에셋 경로까지 하나의 설정 파일(config.ts)로 빼버렸다.
// 디자인 가이드를 수정하는 데 쓰일 가이드 파일
export const PRESENTATION_CONFIG = {
slides: [
{
type: 'TitleSlide',
title: '우리의 새로운 프로젝트',
subtitle: '디자이너와 개발자의 완벽한 협업',
},
{
type: 'ContentSlide',
content: 'GSAP과 데이터 중심 설계로 모든 것을 바꿨습니다.',
image: '/static/images/diagram.png',
},
// ... 슬라이드를 필요한 만큼 추가
],
styles: {
backgroundColor: '#1a1a1a',
fontColor: '#ffffff',
// ... 전역 스타일 정의
},
};
config.ts
이제 디자인 변경만 필요한 경우는 config.ts 파일만 열어서 텍스트를 바꾸고, 이미지 경로를 수정하고, 색깔을 고르면 된다. 코드 구조? React? TypeScript? 알 필요 없다. 그냥 JSON 파일 수정하듯이 하면 끝.
이 두 가지 변화로 프로젝트 구조는 완전히 새로워졌다. 데이터 흐름을 그려보면 대충 이렇다.
graph TD
A[config.ts - 디자인 편집 영역] --> B[메인 템플릿 컴포넌트]
B -- slides 데이터 --> E[Series 컴포넌트]
E -- map() --> F[개별 슬라이드 렌더링]
F -- props --> G[재사용 가능한 슬라이드 컴포넌트]
subgraph "애니메이션 로직"
G --> C[useGsapTimeline 훅]
C --> D[GSAP 타임라인 생성]
end
subgraph "스타일 및 반응형"
A -- styles 데이터 --> K[스타일 Props]
K --> G
A -- 화면 비율 설정 --> M[반응형 레이아웃]
M --> G
end
config.ts가 모든 것의 시작점이자 유일한 진실의 원천(Single Source of Truth)이 된 거다. 파일 구조도 훨씬 단순하고 예측 가능하게 바뀌었다.
파일 구조
ProjectRoot/
├── src/remotion/
│ ├── SalesPresentation/
│ │ ├── config.ts ← 디자인 편집 영역
│ │ ├── SalesPresentation.tsx ← 메인 컴포넌트
│ │ └── components/
│ │ ├── ContentSlide.tsx ← 재사용 슬라이드
│ │ └── Background.tsx ← 배경 컴포넌트
│ ├── TeamIntro/
│ │ ├── TeamMemberSlideGsap.tsx ← GSAP 기반 슬라이드
│ │ └── TeamIntroSequence.tsx ← 시퀀스 컴포넌트
│ └── utils/
│ ├── useGsapTimeline.tsx ← GSAP 훅
│ └── CaptionRenderer.tsx ← 자막 렌더러
└── public/static/
├── audio/
├── images/
└── captions/
다이어그램
위에서 말한 모든 내용들을 다이어그램으로 요약하면 대충 이러하다ㅋ.ㅋ
graph TD
A[Config.ts - 디자인 편집 영역] --> B[Template Component]
B --> C[GSAP Timeline Hook]
C --> D[Animation Refs]
E[Series Component] --> F[Individual Slides]
F --> G[Reusable Slide Component]
G --> H[Props Interface]
I[Audio/Caption Files] --> J[Caption Renderer]
J --> K[Style Props]
A --> L[Aspect Ratio Styles]
L --> M[Responsive Layout]
subgraph "Data Flow"
N[slidesData Array] --> O["map() → Series.Sequence"]
O --> P[Individual Slide Props]
P --> Q[Rendered Components]
end
발견한 이슈들 - 요약과 해결책
1 TypeScript 타입 안정성 문제
- props 인터페이스 불일치
- 타입 정의 누락
타입 안정성 → 인터페이스 정의 및 타입 가드
interface ToolbarProps {
onToolSelect: (toolId: ToolType) => void;
onUpload: (file: File) => void;
activeTool: ToolType | null;
setActiveTool: (tool: ToolType | null) => void;
}
2 과도한 컴포넌트 복잡성
- Remotion의 과도한 파일 분할
- 불필요한 애니메이션 시스템 사용
복잡성 → GSAP 타임라인 단순화
useGsapTimeline(() => {
const tl = gsap.timeline();
// 모든 애니메이션을 하나의 타임라인으로 통합
return tl;
});
3 접근성 문제
- 하드코딩된 스타일/데이터
- 코드 구조 이해 필요
접근성 → 데이터 중심 설계
// 편집 영역
const PRESENTATION_CONFIG = {
slides: [...],
styles: {...}
};
다른 리모션 포스팅을 보고 싶다면
2025-01-16-implementing-canvas-animation-to-remotion-kr
2025-03-18-making-my-own-video-editor-in-progress
KR-no-more-adobe-starting-remotion-kr
KR-making-chart-data-journalism-storytelling-motion-setup-with-remotion
KR-figma-to-jitter-and-remotion
KR-converting-bodymovin-animation-to-remotion-template
KR-how-i-make-a-youtube-series-with-remotion-dynamic-srt-script-setup-exp-likejennie
KR-how-i-made-podcast-interview-with-ai-virtual-idol
KR-remotion-and-shader
[[ _notes/KR/WebDevelopment/KR-thepain-of-animation-debugging ]]