Framer Motion 완벽 가이드 | React 애니메이션·Variants·Gesture·Layout·실전 활용
이 글의 핵심
Framer Motion으로 부드러운 애니메이션을 구현하는 완벽 가이드입니다. Variants, Gesture, Layout Animation, Scroll까지 실전 예제로 정리했습니다.
실무 경험 공유: CSS 애니메이션을 Framer Motion으로 전환하면서, 코드가 60% 감소하고 애니메이션 품질이 크게 향상된 경험을 공유합니다.
들어가며: “애니메이션이 어려워요”
실무 문제 시나리오
시나리오 1: CSS 애니메이션이 복잡해요
keyframes는 번거롭습니다. Framer Motion은 간단합니다.
시나리오 2: 인터랙션이 부족해요
정적인 UI입니다. Framer Motion은 풍부한 인터랙션을 제공합니다.
시나리오 3: 성능이 걱정돼요
잘못된 애니메이션은 느립니다. Framer Motion은 최적화되어 있습니다.
1. Framer Motion이란?
핵심 특징
Framer Motion은 React 애니메이션 라이브러리입니다.
주요 장점:
- 간단한 API: 선언적 문법
- Variants: 재사용 가능한 애니메이션
- Gesture: Drag, Hover, Tap
- Layout Animation: 자동 레이아웃 애니메이션
- 성능: GPU 가속
2. 설치 및 기본 사용
설치
npm install framer-motion
기본 애니메이션
import { motion } from 'framer-motion';
export default function Box() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
Hello Framer Motion!
</motion.div>
);
}
3. Variants
기본 Variants
const variants = {
hidden: { opacity: 0, y: 50 },
visible: { opacity: 1, y: 0 },
};
export default function Box() {
return (
<motion.div
initial="hidden"
animate="visible"
variants={variants}
transition={{ duration: 0.5 }}
>
Content
</motion.div>
);
}
자식 애니메이션
const container = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
export default function List() {
return (
<motion.ul variants={container} initial="hidden" animate="visible">
{items.map((item) => (
<motion.li key={item.id} variants={item}>
{item.name}
</motion.li>
))}
</motion.ul>
);
}
4. Gesture
Hover
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
>
Click me
</motion.button>
Drag
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
dragElastic={0.2}
>
Drag me
</motion.div>
Tap
<motion.button
whileTap={{ scale: 0.9 }}
onTap={() => console.log('Tapped!')}
>
Tap me
</motion.button>
5. Layout Animation
자동 레이아웃
import { motion } from 'framer-motion';
import { useState } from 'react';
export default function ExpandableCard() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
layout
onClick={() => setIsOpen(!isOpen)}
style={{
padding: '1rem',
border: '1px solid #ccc',
borderRadius: '8px',
}}
>
<motion.h2 layout>Title</motion.h2>
{isOpen && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Content goes here...
</motion.p>
)}
</motion.div>
);
}
AnimatePresence
import { AnimatePresence, motion } from 'framer-motion';
export default function List() {
const [items, setItems] = useState([1, 2, 3]);
return (
<ul>
<AnimatePresence>
{items.map((item) => (
<motion.li
key={item}
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 50 }}
>
Item {item}
</motion.li>
))}
</AnimatePresence>
</ul>
);
}
6. Scroll Animation
useScroll
import { motion, useScroll, useTransform } from 'framer-motion';
export default function ScrollAnimation() {
const { scrollYProgress } = useScroll();
const opacity = useTransform(scrollYProgress, [0, 1], [1, 0]);
const scale = useTransform(scrollYProgress, [0, 1], [1, 0.5]);
return (
<motion.div style={{ opacity, scale }}>
Scroll to see animation
</motion.div>
);
}
useInView
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
export default function FadeInWhenVisible() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5 }}
>
Fade in when visible
</motion.div>
);
}
7. 실전 예제
모달
import { motion, AnimatePresence } from 'framer-motion';
export default function Modal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0, 0, 0, 0.5)',
}}
/>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
background: 'white',
padding: '2rem',
borderRadius: '8px',
}}
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
);
}
정리 및 체크리스트
핵심 요약
- Framer Motion: React 애니메이션
- 간단한 API: 선언적 문법
- Variants: 재사용 가능
- Gesture: Drag, Hover, Tap
- Layout Animation: 자동
- Scroll: useScroll, useInView
구현 체크리스트
- Framer Motion 설치
- 기본 애니메이션 구현
- Variants 정의
- Gesture 추가
- Layout Animation 구현
- Scroll Animation 구현
- 성능 최적화
같이 보면 좋은 글
- shadcn/ui 완벽 가이드
- Tailwind CSS 완벽 가이드
- React 완벽 가이드
이 글에서 다루는 키워드
Framer Motion, Animation, React, UI, UX, Frontend, Performance
자주 묻는 질문 (FAQ)
Q. CSS 애니메이션과 비교하면 어떤가요?
A. Framer Motion이 훨씬 간단하고 강력합니다. CSS는 더 가볍지만 제한적입니다.
Q. 성능은 어떤가요?
A. 매우 좋습니다. GPU 가속을 사용하고 최적화되어 있습니다.
Q. Next.js에서 사용할 수 있나요?
A. 네, 완벽하게 호환됩니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, Vercel, Stripe 등 많은 기업에서 사용합니다.