React 애니메이션, Framer Motion vs GSAP: 뭘로 최적화할까요?
React로 웹사이트를 만들다 보면 꼭 욕심나는 게 있죠. 바로 '애니메이션'입니다. 사용자의 눈을 사로잡는 부드러운 인터랙션을 구현할 때, Framer Motion과 GSAP는 아마 가장 먼저 떠오르는 선택지일 겁니다.
두 라이브러리 모두 정말 훌륭하지만, 사실 성능을 최적화하는 접근 방식이 완전히 다릅니다.
- Framer Motion은 "React 컴포넌트처럼" 생각하고, React 생태계 안에서 똑똑하게 움직이도록 최적화되어 있어요.
- GSAP는 "초고성능 엔진"을 React의 렌더링 사이클에 방해되지 않게 '안전하게' 연결하는 데 집중하죠.
이번 글에서는 두 라이브러리가 어떤 차별화된 방법으로 성능을 잡는지, 좀 더 파고들어 보겠습니다.
1. Framer Motion: React와 한 몸처럼 움직이는 최적화
Framer Motion은 애니메이션을 React의 선언적인 방식 그대로 다룹니다. 최적화 전략도 React 개발자라면 "아, 이 문제!" 할 만한 것들을 해결하는 데 초점을 맞추고 있죠.
핵심 전략 1: LazyMotion (가벼운 시작을 위한 코드 분리)
Framer Motion의 가장 강력한 무기 중 하나입니다. 애니메이션 기능을 전부 다 불러오는 게 아니라, 딱 필요한 기능만 골라서 불러올 수 있게 해줍니다.
- 어떻게 하냐면요?
LazyMotion컴포넌트로 애니메이션을 쓸 컴포넌트를 감싸주고,domAnimation(가벼운 필수 기능) 같은 기능 묶음을 지정해주는 거예요. - 뭐가 좋을까요? 웹사이트 첫 로딩 속도에 치명적인 '번들 사이즈'를 확 줄여줍니다. 사용자가 당장 쓰지도 않을 드래그 기능 때문에 첫 페이지가 버벅댈 필요가 없는 거죠.
import { LazyMotion, domAnimation, m } from "framer-motion";
function MyComponent() {
return (
<LazyMotion features={domAnimation}>
{/* m.div에 필요한 최소 기능만 쏙! */}
<m.div animate={{ scale: 1.5 }} />
</LazyMotion>
);
}
핵심 전략 2: 알아서 해주는 하드웨어 가속
Framer Motion은 기본적으로 transform이나 opacity처럼 성능에 유리한 속성들을 사용해 애니메이션을 만듭니다.
- 어떻게 하냐면요? 이런 속성들은 브라우저가 레이아웃을 다시 계산할 필요 없이 GPU가 바로 처리해줘서 아주 빠릅니다.
- 뭐가 좋을까요? 개발자가 일일이 신경 쓰기 귀찮은
will-change같은 CSS 속성을 애니메이션이 시작할 때 알아서 켜고, 끝나면 알아서 꺼줍니다. 덕분에 성능은 챙기면서 메모리 낭비는 막아주죠.
핵심 전략 3: AnimatePresence (사라질 때의 미학)
React에서 컴포넌트가 사라질 때(Unmount) 애니메이션을 넣는 건 꽤나 골치 아픈 일입니다. AnimatePresence는 이 과정을 정말 우아하게 해결해 줍니다.
- 어떻게 하냐면요? 컴포넌트가 DOM에서 사라지기 전에,
exit애니메이션이 끝날 때까지 잠깐 기다려줍니다. - 뭐가 좋을까요?
useEffect나setTimeout으로 복잡하게 타이밍을 맞출 필요가 없어집니다. React의 생명주기와 애니메이션을 자연스럽게 연결해 줘서 코드가 정말 깔끔해져요.
2. GSAP: React를 방해하지 않는 고성능 엔진
GSAP는 React와는 별개로 움직이는, 정말 빠른 JavaScript 애니메이션 엔진입니다. React에서 GSAP를 최적화한다는 건, 이 강력한 엔진이 React의 리렌더링을 방해하지 않고 메모리 누수 없이 깔끔하게 작동하도록 만드는 게 핵심입니다.
핵심 전략 1: useGSAP (안전한 사용과 자동 청소)
React 컴포넌트 안에서 GSAP를 쓰다 보면 가장 무서운 게 메모리 누수입니다. 컴포넌트는 사라졌는데, 애니메이션은 계속 실행되려고 하는 상황이죠.
- 어떻게 하냐면요?
@gsap/react패키지의useGSAP훅을 사용하면 됩니다. 이 훅 안에서 만든 모든 애니메이션은 자동으로 '컨텍스트'라는 곳에 등록돼요. - 뭐가 좋을까요?
useEffect의 클린업 함수처럼, 컴포넌트가 사라질 때 등록됐던 모든 애니메이션을 알아서.kill()(제거) 해줍니다. 개발자가 깜빡하고 청소 코드를 빼먹어서 생기는 문제를 원천 봉쇄해 주죠. 정말 마음이 편해집니다.
import { useGSAP } from "@gsap/react";
import { useRef } from "react";
function MyComponent() {
const container = useRef();
// 이 안에서 만든 GSAP 애니메이션은
// 컴포넌트가 사라질 때 자동으로 청소됩니다.
useGSAP(
() => {
gsap.to(".box", { x: 300 });
},
{ scope: container } // scope로 범위를 좁혀주는 센스!
);
return (
<div ref={container}>
<div className="box"></div>
</div>
);
}
핵심 전략 2: ref로 DOM 직접 제어 (React 리렌더링 우회)
GSAP가 압도적인 성능을 내는 비결은 React의 렌더링 과정을 살포시 비켜가는 데 있습니다.
- 어떻게 하냐면요? React의
state를 바꿔서 애니메이션을 주는 게 아니라,useRef로 가져온 실제 DOM 요소(ref.current)에 직접 스타일을 적용합니다. - 뭐가 좋을까요? 애니메이션 매 프레임마다
state가 바뀌면, React는 계속해서 리렌더링을 하느라 바빠집니다. GSAP는 React 몰래(?) DOM을 직접 움직이기 때문에, 이런 비싼 렌더링 비용이 전혀 들지 않아요. 수백 개 요소가 동시에 춤추는 복잡한 애니메이션도 거뜬히 소화하는 이유죠.
그래서, 제 프로젝트엔 뭘 써야 할까요?
정리하자면 이렇습니다.
Framer Motion이 끌린다면:
React의 선언적인 방식이 너무 편하고, 컴포넌트처럼 다루고 싶을 때.
버튼 호버, 모달 등장, 페이지 전환 같은 일반적인 UI 인터랙션이 주력일 때.
**초기 로딩 속도(번들 사이즈)**가 무엇보다 중요할 때 (
LazyMotion).GSAP가 끌린다면:
스크롤에 따라 펼쳐지는 복잡한 스토리텔링 애니메이션이 필요할 때.
수백, 수천 개의 요소가 동시에 움직여야 하는 최고 수준의 성능이 필요할 때.
ScrollTrigger같은 강력한 플러그인을 활용해 역동적인 사이트를 만들고 싶을 때.
'무조건 이게 더 좋다'는 건 없습니다. 내 프로젝트가 **'첫 로딩 속도'**가 더 급한지, 아니면 **'복잡한 애니메이션의 런타임 성능'**이 더 중요한지 고민해보고, 딱 맞는 라이브러리를 선택하는 게 베스트입니다.
🔗 더 깊게 파고들기 (공식 문서)
이 글을 읽고 더 궁금해지셨다면, 공식 문서를 확인해 보세요.
Framer Motion
- LazyMotion (코드 스플리팅):
LazyMotion으로 번들 사이즈 줄이는 방법. - AnimatePresence (컴포넌트 제거):
exit애니메이션을 다루는 방법. - 성능 가이드 (하드웨어 가속): Framer Motion이 성능을 챙기는 방식.
GSAP
- GSAP ❤️ React (React에서 GSAP 쓰기):
useGSAP훅과 메모리 관리에 대한 필수 가이드. - [
useGSAP()훅 API](<https: //www.google.com/search?q=%5Bhttps://gsap.com/docs/v3/React/useGSAP/%5D(https://gsap.com/docs/v3/React/useGSAP/)>):useGSAP훅의 상세한 옵션들.- [
gsap.context()](<https: //www.google.com/search?q=%5Bhttps://gsap.com/docs/v3/GSAP/gsap.context/%5D(https://gsap.com/docs/v3/GSAP/gsap.context/)> ):useGSAP의 핵심 원리이자, 메모리 관리를 위한 GSAP의 기능.
- [