
ํ๋ก ํธ์๋์์ ๊ฐ์๊ธฐ ๋ฌผ๋ฆฌํ์ด ์ ๋์?
ํ๋ก ํธ์๋์์ ๊ฐ์๊ธฐ ๋ฌผ๋ฆฌํ์ด ์ ๋์? ๊ด๋ จ
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ก์ ๊ฐ์ฅ ๋ณด๋์ฐฌ ์๊ฐ์ ๋์์ด๋์ "์ด๊ฑฐ ์ด๋ ค์ธ ๊ฒ ๊ฐ์๋ฐ..."๋ผ๋ ๋ง์ "ํด๋ณผ๊ฒ์"๋ผ๊ณ ๋๋ตํ๊ณ ์๋ฒฝํ ๊ตฌํํด ๋์ ๋์ ๋๋ค. ์ ๋ ๋ณต์กํ ์ธํฐ๋์ ๊ณผ ์ ๋๋ฉ์ด์ ์ ๊ตฌํํ ๋๋ง๋ค ์ฌ์ฉ์ ๊ฒฝํ์ด ํ์ธต ํ๋ถํด์ง๋ ๊ฒ์ ๋๋ผ๋ฉฐ ์ฑ์ฅํฉ๋๋ค. ๊ณผ๊ฑฐ์๋ ์ด๋ฐ ์ ๋๋ฉ์ด์ ์ ๊ตฌํํ๋ ๋ฐ ๋ง์ ์๊ฐ๊ณผ ๋ ธ๋ ฅ์ด ํ์ํ์ง๋ง, ์ต๊ทผ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๊ตฌ๋ค์ ๋ฐ์ ์ผ๋ก ํจ์ฌ ์์ํด์ก์ต๋๋ค.
๊ทธ๋ฐ๋ฐ ์ ๋๋ฉ์ด์ ๊ตฌํ์ ์ํด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฌธ์๋ฅผ ํผ์ณ๋ณด๋ฉด, ์ด์ํ ์ฉ์ด๊ฐ ๋์ ๋ค์ด์ต๋๋ค. "spring"? ๋ด? ์๋๋ฉด ์ฉ์์ฒ ? ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๊ณตํต๋๋ ๋ณ์๋ช ๋ค, ์ด๊ฒ๋ค์ด ์ ์น ๊ฐ๋ฐ ๋ฌธ์์ ๋ฑ์ฅํ๋ ๊ฑธ๊น์?
// Framer Motion ์์
<motion.div
animate={{ x: 100 }}
transition={{ type: "spring", stiffness: 100, damping: 10 }}
/>
// React Spring ์์
const springs = useSpring({
from: { x: 0 },
to: { x: 100 },
config: { mass: 1, tension: 170, friction: 26 }
})
// Svelte Motion ์์
<script>
import { Spring } from 'svelte/motion';
let coords = new Spring({ x: 50, y: 50 }, {
stiffness: 0.1,
damping: 0.25
});
let size = new Spring(10);
</script>
์ฌ๊ธฐ์ stiffness
, damping
, mass
, tension
, friction
๊ฐ์ ๋ฌผ๋ฆฌํ ์ฉ์ด๋ค์ด ๋ณด์ด์๋์? ์ค์ ์ธ๊ณ์ ์ ์ฌํ๊ฒ ์์ง์ด๋ ์ ๋๋ฉ์ด์
์ ์ฌ์ฉ์ ๋ง์กฑ๋๋ฅผ ๋์ด๊ณ ์ ํ์จ์ ๊ฐ์ ํ๋ ๋ฑ ์ค์ง์ ์ธ ๋น์ฆ๋์ค ๊ฐ์น๋ฅผ ์ฐฝ์ถํฉ๋๋ค. ์ด๋ฌํ ์์ฐ์ค๋ฌ์ด ์์ง์์ ๋น๋ฐ์ ๋ฐ๋ก โ๋ฌผ๋ฆฌํโ์ ์์ต๋๋ค. ํ๋ก ํธ์๋์ ๊ฐ์๊ธฐ ๋ฌผ๋ฆฌํ์ด ์ ๋์? ์ถ์ ์ ์์ง๋ง, ์ด ๊ธ์์๋ ์ ์คํ๋ง ์ด๋์ด ์น ์ ๋๋ฉ์ด์
์ ๋ฑ์ฅํ๋์ง๋ฅผ ์๊ฐํ๊ณ , ๊ตฌํ ์๋ฆฌ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
CSS๋ก๋ ์ ๋๋ ๊ฑธ๊น?
CSS ์ ๋๋ฉ์ด์ ์ ์น ๊ฐ๋ฐ์ ๊ธฐ๋ณธ์ด์ง๋ง, ์ฌ๋ฌ ํ๊ณ์ ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. 2015๋ ๋ฐํ๋ "The State of Animation in React"์์๋ CSS ์ ๋๋ฉ์ด์ ์ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ ๋ฅผ ๋ช ํํ ์ง์ ํ๊ณ , ์ด๊ฒ์ด ์ดํ ํ๋ก ํธ์๋ ์ ๋๋ฉ์ด์ ์ ํจ๋ฌ๋ค์์ ๋ฐ๊พธ๋ ์๋ฐ์ ์ด ๋์์ต๋๋ค.

CSS๋ ์๊ฐ ๊ธฐ๋ฐ์ด๋ผ ์์ฐ์ค๋ฝ์ง ์๋ค
CSS ์ ๋๋ฉ์ด์ ์ ์๊ฐ(duration)์ ๊ธฐ๋ฐ์ผ๋ก ์๋ํฉ๋๋ค. ๋น ๋ฅธ ์ ๋๋ฉ์ด์ ์ ์ํ๋ฉด ์๊ฐ์ ์งง๊ฒ, ๋๋ฆฐ ์ ๋๋ฉ์ด์ ์ ์ํ๋ฉด ์๊ฐ์ ๊ธธ๊ฒ ์ค์ ํ์ฃ . ๊ทธ๋ฆฌ๊ณ ์๊ฐ ๋ด์ ๊ฐ ๋ณํ๋ฅผ ๋ฒ ์ง์ด ๊ณก์ (cubic-bezier)์ผ๋ก ํต์ ํฉ๋๋ค.
.box {
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
ํ์ง๋ง ์ด ์ ๊ทผ๋ฒ์๋ ์ฌ๊ฐํ ๋ฌธ์ ๋ค์ด ์์ต๋๋ค.
- ๋ฒ ์ง์ด ๊ณก์ ์ ๋ํดํจ: ์ง๊ด์ ์ด์ง ์๊ณ ์ํ๋ ํจ๊ณผ๋ฅผ ์ ํํ ์ป๊ธฐ ์ํด์๋ ๋ง์ ์ํ์ฐฉ์ค๊ฐ ํ์ํฉ๋๋ค. ์ด๋ค ๊ณก์ ์ด ์์ฐ์ค๋ฌ์ด ์์ง์์ ๋ง๋ค์ด๋ผ์ง ์์ธกํ๊ธฐ ์ด๋ ต์ต๋๋ค.
- ๊ฑฐ๋ฆฌ์ ์๊ฐ์ ๋ถ์ผ์น: ๊ฐ์ฅ ์น๋ช ์ ์ธ ๋ฌธ์ ์ ๋๋ค. ๊ฐ์ ๋ณํํด์ผ ํ๋ ์(๊ฑฐ๋ฆฌ)์ ๋ฐ๋ผ ์๋๊ฐ ๋ฌ๋ผ์ง๋๋ค. ์๊ฐ์ ์งง๊ฒ ์ค์ ํด๋ ๋ณํ๋์ด ์ ๋ค๋ฉด ๋๋ฆฌ๊ฒ ํํ๋๊ณ , ๋ณํ๋์ด ๋ง๋ค๋ฉด ๋น ๋ฅด๊ฒ ํํ๋ฉ๋๋ค.
- ์ ๋๋ฉ์ด์ ์ค๋จ ์ ๋ถ์์ฐ์ค๋ฌ์: ์งํ ์ค์ธ ์ ๋๋ฉ์ด์ ์ด ์ค๋จ๋๊ฑฐ๋ ๋ฐฉํฅ์ด ๋ฐ๋ ๋, ์๋๊ฐ ๊ฐ์๊ธฐ ๋ณํ์ฌ ๋ถ์์ฐ์ค๋ฌ์ด ๋ชจ์ ์ด ๋ง๋ค์ด์ง๋๋ค. ์ค์ ๋ฌผ๋ฆฌ ์ธ๊ณ์์๋ ๊ด์ฑ์ด ์ ์ง๋์ง๋ง, CSS์์๋ ์ด๋ฅผ ํํํ๊ธฐ ์ด๋ ต์ต๋๋ค.
/* ์ด ๋ ์์๋ ๊ฐ์ ์๊ฐ(2์ด)์ด ๊ฑธ๋ฆฌ์ง๋ง ์๋ ๋๋์ด ์์ ํ ๋ค๋ฆ
๋๋ค */
.short-distance {
animation: move-100px 2s ease;
}
.long-distance {
animation: move-500px 2s ease;
}
ํนํ 2๋ฒ์ ๊ฒฝ์ฐ, ๋ฌดํ ๋ฐฐ๋(infinite banner)๋ฅผ ๊ตฌํํ ๋ ๋ฌธ์ ๊ฐ ๊ทน๋ช ํ๊ฒ ๋๋ฌ๋ฉ๋๋ค. ํ๋ฉด์ด ์์ ๋ชจ๋ฐ์ผ์์๋ ๋ฐฐ๋๊ฐ ์ฒ์ฒํ ์์ง์ด๊ณ , ๊ฐ์ ์ฝ๋๊ฐ ํฐ ํ๋ฉด์์๋ ๋ฐฐ๋๊ฐ ๋น ๋ฅด๊ฒ ์ง์ฃผํ๊ฒ ๋ฉ๋๋ค. ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๊ธฐ ์ํด ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ง๋ค ๋ค๋ฅธ ์๊ฐ์ ์ค์ ํด์ผ ํ๋ ๋นํจ์จ์ด ๋ฐ์ํฉ๋๋ค.

"๋ฌผ๋ฆฌํ์ ์ ์ฉํด ๋ณด๋ฉด ์ด๋จ๊น?"
์ด๋ฐ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ๊ฒ์ด ๋ฐ๋ก "Spring" ๋ชจ๋ธ์ ๋๋ค. Spring์ ์ฉ์์ฒ ์ ์์ง์์ ๋ชจ๋ฐฉํ ๋ชจ๋ธ๋ก, ์ค์ ๋ฌผ๋ฆฌ ์ธ๊ณ์ ๋ฒ์น์ ์น ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ์ต๋๋ค. React-spring ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์์ผ๋ก, Framer Motion, PopMotion, svelte/motion ๋ฑ ํ๋ ํ๋ก ํธ์๋์ ๋ํ์ ์ธ ์ ๋๋ฉ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ชจ๋ ์ด Spring ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค.
Spring ๋ชจ๋ธ์ ๋ง์น ์ฉ์์ฒ ์ ๋ฌ๋ฆฐ ์ถ์ ์์ง์์ผ๋ก ๋ชจ๋ ๋น ๋ฅด๊ธฐ๋ฅผ ๋ํ๋ ๋๋ค. ํต์ฌ์ ์ธ ๊ฐ์ง ๋ฌผ๋ฆฌ์ ํ๋ผ๋ฏธํฐ์ ๋๋ค.
- Stiffness(ํ์ฑ๊ณ์): ์ฉ์์ฒ ์ด ์ผ๋ง๋ ํ์ฑ์ด ์๋์ง ๋ํ๋ ๋๋ค. ๊ฐ์ด ๋์์๋ก ์ฉ์์ฒ ์ด ๋ ๋จ๋จํด์ ธ ๋น ๋ฅด๊ณ ํ๋ฐํ๊ฒ ์์ง์ ๋๋ค.
- Damping(๋ง์ฐฐ๊ณ์): ์ค์ ์ธ๊ณ์์๋ ๊ณต๊ธฐ๋ ๋ฌผ ๋ฑ์ ์ํด ๋ฌผ์ฒด์ ์์ง์๊ณผ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ์ ํญํ๋ ํ์ด ์์ฉํฉ๋๋ค. ๊ทธ๋์ ์ฉ์์ฒ ์ ๋งค๋ฌ๋ฆฐ ๊ณต์ ๊ฒฐ๊ตญ ์ด๋์ด ๋ฉ์ถ๊ฒ ๋ฉ๋๋ค. ๊ฐ์ด ๋์์๋ก ๋นจ๋ฆฌ ์์ ํ๋ฉ๋๋ค.
- Mass(์ง๋): ๋ฌผ์ฒด์ ๋ฌด๊ฒ๋ฅผ ๋ํ๋ ๋๋ค. ๊ฐ์ด ํด์๋ก ์์ง์์ด ๋๋ ค์ง๋๋ค. ๊ฐ์ ํ์ ๋ฐ์๋ ๋ฌด๊ฑฐ์ด ๋ฌผ์ฒด๋ ๊ฐ๋ฒผ์ด ๋ฌผ์ฒด๋ณด๋ค ์ฒ์ฒํ ์์ง์ด์ฃ .
// React Spring ์์
useSpring({
from: { x: 0 },
to: { x: 300 },
config: {
mass: 1, // ์ง๋
tension: 170, // ํ์ฑ๊ณ์(stiffness)
friction: 26 // ๋ง์ฐฐ๊ณ์(damping)
}
})
Spring ๋ชจ๋ธ์ ์ฅ์ ๋ค
- ๊ฑฐ๋ฆฌ ๋ ๋ฆฝ์ ์ผ๊ด์ฑ: ์ด๋ ๊ฑฐ๋ฆฌ๊ฐ ๋ฌ๋ผ๋ ๊ฐ์ ๋ฌผ๋ฆฌ์ ๋๋์ ์ ์งํฉ๋๋ค. ๋ฌดํ ๋ฐฐ๋๊ฐ ์ด๋ค ํ๋ฉด ํฌ๊ธฐ์์๋ ์ผ๊ด๋ ์๋๊ฐ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
- ์์ฐ์ค๋ฌ์ด ์ค๋จ๊ณผ ๋ฐฉํฅ ์ ํ: ์ ๋๋ฉ์ด์ ์ด ์ค๊ฐ์ ์ค๋จ๋๊ฑฐ๋ ๋ชฉํ๊ฐ ๋ฐ๋์ด๋, ํ์ฌ ์๋(๊ด์ฑ)๋ฅผ ์ ์งํ๋ฉด์ ์๋ก์ด ๋ชฉํ๋ก ๋ถ๋๋ฝ๊ฒ ์ ํ๋ฉ๋๋ค.
- ์ง๊ด์ ์ธ ์กฐ์ : ๋ฌผ๋ฆฌ์ ๊ฐ๋ ์ ๋ฐํ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์, ํ๋ผ๋ฏธํฐ ์กฐ์ ์ด ์์ธก ๊ฐ๋ฅํฉ๋๋ค. "๋ ๋ฌด๊ฒ๊ฒ" ๋๋ "๋ ํ๋ ฅ ์๊ฒ"์ ๊ฐ์ ์ง๊ด์ ์ธ ์ฌ๊ณ ๋ก ์ํ๋ ํจ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ Spring ๋ชจ๋ธ์ CSS ์ ๋๋ฉ์ด์ ์ ํ๊ณ๋ฅผ ๋ฐ์ด๋์ด, ์น์์๋ ์ค์ ์ธ๊ณ์ ๊ฐ์ ์์ฐ์ค๋ฌ์ด ์์ง์์ ๊ฐ๋ฅ์ผ ํ์ต๋๋ค. ์ด์ ๊ฑฐ์ ๋ชจ๋ ๋ชจ๋ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ์์ Spring ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์ ์ ํ์์ ์ธ ์์๊ฐ ๋์์ต๋๋ค.
์์นํด์์ผ๋ก ์ดํดํ๋ Spring ์ ๋๋ฉ์ด์
๋ํ๊ต์์ ๋ฌผ๋ฆฌ๊ต์ก์ ์ ๊ณตํ๋ ์ ๋ ์ด๋ฐ ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์ ์๋ฆฌ๋ฅผ ์ดํด๋ณด๋ ๊ฒ์ ์ข์ํฉ๋๋ค. ์ค์ ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ์์ ์ด๋ป๊ฒ ์๋ํ๋์ง ์ดํดํ๋ฉด, ๋ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ด์ฃ .
๋ดํด ๋ฒ์น๊ณผ ์ด๋๋ฐฉ์ ์
๋ฌผ์ฒด๋ ์ด๋ค ํ์ ๋ฐ๋๋์ ๋ฐ๋ผ ์์ง์์ด ๋ฌ๋ผ์ง๋๋ค. ์ด ํ์ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์๋ ์๊ณ , ๋ฌผ์ฒด์ ์๋์ ๋ฐ๋ผ์๋ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค. ๋ดํด์ ์ 2๋ฒ์น()์ ๋ฐ๋ฅด๋ฉด, ๋ฌผ์ฒด์ ํ์ด ๊ฐํด์ง๋ฉด ๊ฐ์๋๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด ํ๊ณผ ๋ฌผ์ฒด์ ์ฌ๋ฌ ์์๋ค ๊ฐ์ ๊ด๊ณ๋ฅผ ์ํ์ ์ผ๋ก ํํํ ๊ฒ์ด ๋ฐ๋ก ์ด๋๋ฐฉ์ ์์ ๋๋ค.
Spring ๋ชจ๋ธ์์์ ์ด๋๋ฐฉ์ ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฌ๊ธฐ์
- ๋ ํ
- ์ ์ง๋
- ๋ ๊ฐ์๋
- ๋ ํ์ฑ๊ณ์(stiffness)
- ๋ ํํ ์์น๋ก๋ถํฐ์ ๋ณ์
- ๋ ๋ง์ฐฐ๊ณ์(damping)
- ๋ ์๋
๊ณ ๋ฑํ๊ต ๋ฌผ๋ฆฌ์์๋ ๋จ์ํ๋ ๋ง ๋ฐฐ์ฐ์ง๋ง, ํ์ค์์๋ ์ ํญ๋ ฅ๊น์ง ๊ผญ ํฌํจํด์ผ ํฉ๋๋ค. ์ ํญ์ ์๋์ ๋น๋กํด์ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ์์ฉํ๊ธฐ ๋๋ฌธ์ ํญ์ด ์ถ๊ฐ๋ฉ๋๋ค.
์์นํด์์ผ๋ก ํธ๋ ์ด๋๋ฐฉ์ ์
์ด๋ฐ ๋ฏธ๋ถ๋ฐฉ์ ์์ ๊ฐ๋จํ ๊ฒฝ์ฐ ํด์์ ์ผ๋ก ํ ์๋ ์์ง๋ง, ๋น์ ํ ๋ฐฉ์ ์์ ์ ํํ ํด๋ฅผ ๊ตฌํ๊ธฐ ์ด๋ ค์ด ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ด๋ด ๋ ์์นํด์๋ฒ์ด ์ ์ฉํฉ๋๋ค.
์์นํด์์ ๊ธฐ๋ณธ ์์ด๋์ด๋ ๊ฐ๋จํฉ๋๋ค.
- ์๊ฐ์ ์์ฃผ ์์ ์กฐ๊ฐ(
dt
)์ผ๋ก ๋๋๋๋ค. - ๊ฐ ์๊ฐ ์กฐ๊ฐ ๋์์ ๋ฌผ์ฒด๊ฐ ์ง์ ๋ฑ์์ด๋์ ํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
- ๊ฐ ๋จ๊ณ๋ง๋ค ์๋ก์ด ์์น์ ์๋๋ฅผ ๊ณ์ฐํ๊ณ , ์ด๋ฅผ ๋ค์ ๋จ๊ณ์ ์ถ๋ฐ์ ์ผ๋ก ์ผ์ต๋๋ค.
- ์ด ๊ณผ์ ์ ๋ฐ๋ณตํด ์ ์ฒด ์์ง์์ ๊ทผ์ฌํฉ๋๋ค.
์น์์๋ requestAnimationFrame
์ ์ฌ์ฉํ์ฌ ํ๋ฉด ๊ฐฑ์ ์ฃผ๊ธฐ๋ง๋ค ์ด ๊ณ์ฐ์ ์ํํ ์ ์์ต๋๋ค.
Spring ์ ๋๋ฉ์ด์ ์ ์์นํด์ ๊ตฌํ
๊ฐ์ฅ ๋จ์ํ ํํ์ ์์นํด์ ๋ฐฉ๋ฒ(์ค์ผ๋ฌ ๋ฐฉ๋ฒ)์ ์ฌ์ฉํ Spring ๊ตฌํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// ์ด๊ธฐ ์ํ
let position = 0; // ํ์ฌ ์์น
let velocity = 0; // ํ์ฌ ์๋
const target = 100; // ๋ชฉํ ์์น
const stiffness = 0.1; // ํ์ฑ๊ณ์
const damping = 0.6; // ๋ง์ฐฐ๊ณ์
const mass = 1; // ์ง๋
function updateSpring(dt) {
// ํ ๊ณ์ฐ: F = -k(x - target) - cv
const displacement = position - target;
const springForce = -stiffness * displacement;
const dampingForce = -damping * velocity;
const force = springForce + dampingForce;
// ๊ฐ์๋ ๊ณ์ฐ: a = F/m
const acceleration = force / mass;
// ์๋ ์
๋ฐ์ดํธ: v = v + a*dt
velocity += acceleration * dt;
// ์์น ์
๋ฐ์ดํธ: x = x + v*dt
position += velocity * dt;
return position;
}
// ์ ๋๋ฉ์ด์
๋ฃจํ
let lastTime = 0;
function animate(currentTime) {
if (lastTime > 0) {
const dt = (currentTime - lastTime) / 1000; // ์ด ๋จ์๋ก ๋ณํ
const currentPosition = updateSpring(dt);
// DOM ์์์ ์์น ์
๋ฐ์ดํธ
element.style.transform = `translateX(${currentPosition}px)`;
}
lastTime = currentTime;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
์ด ์ฝ๋๋ Spring ์์ง์์ ๊ธฐ๋ณธ ์๋ฆฌ๋ฅผ ๋ณด์ฌ์ค๋๋ค.
- ์คํ๋ง์ ์ค์ฌ(target)์ผ๋ก ๋์ด๋น๊ธฐ๋ ํ์ ๊ฐํฉ๋๋ค.
- ์ ํญ์ด ์์ผ๋ฉด ๋ฌผ์ฒด๋ ๋ชฉํ ์ง์ ์ ์ค์ฌ์ผ๋ก ์๋ค๊ฐ๋ค ๋ฐ๋ณตํฉ๋๋ค.
- ์ ํญ์ด ํฌ๋ฉด ํ ๋ฒ์ ๋ชฉํ ์ง์ ์ ๋๋ฌํ๊ณ ๋ฉ์ถฅ๋๋ค.
๋ณด๋ค ์ ํํ ์์นํด์: ๋ฃฝ๊ฒ-์ฟ ํ ๋ฐฉ๋ฒ
์์ ๋จ์ํ ๋ฐฉ๋ฒ(์ค์ผ๋ฌ ๋ฐฉ๋ฒ)์ ์ค์ฐจ๊ฐ ๋์ ๋์ด ์๋์ง๊ฐ ์ฆ๊ฐํ๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ํนํ Spring ๋ชจ๋ธ์์๋ ์๊ฐ์ด ์ง๋ ์๋ก ์ง๋์ด ์ ์ ์ปค์ง๋ ํ์์ด ๋ฐ์ํ ์ ์์ต๋๋ค.

์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ ์ ํํ ์์นํด์ ๋ฐฉ๋ฒ์ธ ๋ฃฝ๊ฒ-์ฟ ํ(Runge-Kutta) ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด ๋ฐฉ๋ฒ์ ๋ ๋ณต์กํ์ง๋ง ํจ์ฌ ์์ ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํฉ๋๋ค.
function rungeKuttaStep(state, dt) {
const x = state.position;
const v = state.velocity;
// Spring ๋ฐฉ์ ์: a = -w^2 * x
function acceleration(pos, vel) {
return -stiffness * pos / mass; // -w^2 * x
}
// k1: ํ์ฌ ์ํ์์์ ๋ํจ์
const k1 = {
v: v,
a: acceleration(x, v)
};
// k2: ์ค๊ฐ์ (dt/2)์์์ ๋ํจ์ ์ฒซ ๊ทผ์ฌ
const k2 = {
v: v + (dt * k1.a) / 2,
a: acceleration(
x + (dt * k1.v) / 2,
v + (dt * k1.a) / 2
)
};
// k3: ์ค๊ฐ์ (dt/2)์์์ ๋ํจ์ ๋๋ฒ์งธ ๊ทผ์ฌ
const k3 = {
v: v + (dt * k2.a) / 2,
a: acceleration(
x + (dt * k2.v) / 2,
v + (dt * k2.a) / 2
)
};
// k4: ๋์ (dt)์์์ ๋ํจ์
const k4 = {
v: v + dt * k3.a,
a: acceleration(
x + k3.v * dt,
v + dt * k3.a
)
};
// ๊ฐ์ค ํ๊ท ์ผ๋ก ์ต์ข
๋ณํ๋ ๊ณ์ฐ
return {
position: x + (dt * (k1.v + 2 * k2.v + 2 * k3.v + k4.v)) / 6,
velocity: v + (dt * (k1.a + 2 * k2.a + 2 * k3.a + k4.a)) / 6,
time: state.time + dt
};
}
๋ฃฝ๊ฒ-์ฟ ํ ๋ฐฉ๋ฒ์ ๊ฐ ์๊ฐ ๋จ๊ณ์์ 4๋ฒ์ ๋ํจ์ ํ๊ฐ๋ฅผ ํตํด ๋ ์ ํํ ๋ค์ ์ํ๋ฅผ ์ถ์ ํฉ๋๋ค. ์๊ฐ ๋จ๊ณ ์ฌ์ด์ ์ค๊ฐ ์ง์ ๋ค์ ํ์ฉํ์ฌ ๊ฐ์ค ํ๊ท ์ ๊ณ์ฐํ๋ ๋ฐฉ์์ด์ฃ .
์ด ๋ฐฉ๋ฒ์ ์ค์ผ๋ฌ ๋ฐฉ๋ฒ๊ณผ ๋ฌ๋ฆฌ ์๋์ง ๋ณด์กด ๋ฒ์น์ ๋ ์ ์ ์งํ๊ธฐ ๋๋ฌธ์, ์ค๋ ์๋ฎฌ๋ ์ด์ ์ ๋๋ ค๋ ์งํญ์ด ๋ถ์์ฐ์ค๋ฝ๊ฒ ์ฆ๊ฐํ๊ฑฐ๋ ๊ฐ์ํ๋ ํ์์ด ํฌ๊ฒ ์ค์ด๋ญ๋๋ค. ์ ํํ ์์นํด์ ๋ฐฉ๋ฒ์ ์ค์ ๋ฌผ๋ฆฌ๊ณ์ ํน์ฑ์ ๋ ์ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์, ๋ ์์ฐ์ค๋ฌ์ด ์ ๋๋ฉ์ด์ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด๋ ๋๋ค.
๊ถค๋ ์ด๋ํ๋ ํ์ฑ
์ด๋ฐ ์์นํด์ ์๋ฆฌ๋ฅผ ์์ฉํด์ ํ์ฑ์ ์์ง์์ ํํํ ์ ์์ต๋๋ค. ์ด๋๋ฐฉ์ ์์ผ๋ก ์ ๊ทผํ๋ฉด, ํ์ฌ์ ์๋์ ์์น๋ฅผ ๋ณด์กดํ๋ฉด์ ์์ฐ์ค๋ฌ์ด ์ ๋๋ฉ์ด์ ๋ณํ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
์ค์ ์ธ๊ณ์์๋ ์๋๊ฐ ๊ฐ์๊ธฐ ๋ณํ์ง ์์ต๋๋ค. ์๋๊ฐ ์๊ฐ์ ๋ฐ๋ผ ๋ณํํ๋ ์ด์ ๋ ํ์ด ์์ฉํ๊ธฐ ๋๋ฌธ์ด๋ฉฐ, ์๋๊ฐ ๊ฐ์๊ธฐ ๋ณํ๋ค๋ฉด ๊ทธ๊ฒ์ ํ์ด ๋ฌดํ๋๋ผ๋ ์๋ฏธ์ ๋๋ค. ๋ฌผ๋ฆฌ ์ธ๊ณ์์ ๋ฌดํ๋์ ํ์ด ์์ฉํ๋ ์ํฉ์ ์กด์ฌํ์ง ์์ฃ .
์ด๋ฌํ ๋ฌผ๋ฆฌ์ ์ง๊ด์ด ์ฐ๋ฆฌ์ ๋ฌด์์์ ๋ฐํ์๊ธฐ ๋๋ฌธ์, CSS์ฒ๋ผ ์๊ฐ ๊ธฐ๋ฐ์ผ๋ก ์ ๋๋ฉ์ด์ ์ด ๋์ ์ผ๋ก ๋ฐ๋ ๋ ์ด์ํจ์ ๋๋ผ๋ ๊ฒ์ ๋๋ค. ์ด๋ ๋ฐฉ์ ์์ ๊ธฐ๋ฐํ ์ ๊ทผ๋ฒ์ ์ด๋ฌํ ๋ถ์์ฐ์ค๋ฌ์์ ํด๊ฒฐํ๊ณ , ์ฌ์ฉ์์ ๋ฌด์์์ ๊ธฐ๋์ ๋ถํฉํ๋ ์์ฐ์ค๋ฌ์ด ์์ง์์ ๋ง๋ค์ด๋ ๋๋ค.

์ด๋๋ฐฉ์ ์ ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ง์ ์จ๋ณด์
์ด๋๋ฐฉ์ ์ ๊ธฐ๋ฐ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ฉ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ๋ฉด ๋์ ์ผ๋ก ๋ณํํ๋ ์ ๋๋ฉ์ด์ ์ ์์ฐ์ค๋ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. ๋ฌผ๋ฆฌ ๋ฒ์น์ ์ฝ๋๋ก ๋ น์ฌ๋ธ ์ด๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ฐ๋ฐ์๊ฐ ๋ณต์กํ ์ํ์ ๊ณ์ฐ์ ์ง์ ๊ตฌํํ ํ์ ์์ด ์์ฐ์ค๋ฌ์ด ์ธํฐ๋์ ์ ๋ง๋ค ์ ์๊ฒ ํด์ค๋๋ค.
React Spring์ผ๋ก ๊ตฌํํ๋ ๋์ ์ธํฐ๋์
React Spring์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์ ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ ์ ์ฝ๋๋ก ์์ฐ์ค๋ฌ์ด ์คํ๋ง ์ ๋๋ฉ์ด์ ์ ๊ตฌํํ ์ ์์ผ๋ฉฐ, ๋ฌด์๋ณด๋ค ์ค์๊ฐ์ผ๋ก ๋ชฉํฏ๊ฐ์ด ๋ณํด๋ ๋ถ๋๋ฝ๊ฒ ์ ํ๋ฉ๋๋ค.

๋ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค ๋๋ ์ ์ฉํ๋ค!
Flitter๋ผ๋ ๋ ๋๋ง ์์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ AnimatedAlign์ด๋ผ๋ ์์ ฏ์ ๊ฐ๋ฐํ์ต๋๋ค. ์ด ์์ ฏ์ ๋ฌผ์ฒด์ ์์น๋ฅผ ์ ๋ ฌ์ด๋ผ๋ ์์ฑ์ผ๋ก ๋ณ๊ฒฝํ ๋ ์๋์ผ๋ก ํธ๋์ง์ ์ ๋๋ฉ์ด์ ์ ๋ถ์ฌํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ํ๋ฉด์ ์กฐ์ํ๋ฉด์ ๋์ ์ผ๋ก ๋ฐ๋๋ ์์น์ ๋ํด์๋ ์์ฐ์ค๋ฝ๊ฒ ์๋์ ์์น๋ฅผ ๊ธฐ๋กํด ์ ์ฉ๋ฉ๋๋ค.

์ฒ์์๋ ์ด๋ ์ ๋๋ฉ์ด์ ์ ์ง์ ๊ตฌํํ ์ง ๊ณ ๋ฏผํ์ต๋๋ค. ํ์ง๋ง ํน์ ํ๋ ์์ํฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ผ ํ ์ง๋ผ๋, ์ฝ์ด ๋ถ๋ถ์ JavaScript๋ก ๋ฐ๋ก ๋ชจ๋ํํ๋ ์ด๋ฅธ๋ฐ "agnostic"(๋ ๋ฆฝ์ ) ๊ตฌํ์ฒด๊ฐ ์์ ๊ฑฐ๋ผ๊ณ ์๊ฐํ์ต๋๋ค. framer-motion๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ๊ณ ๋ค์ด๊ฐ ์ดํด๋ณด๋, popmotion์ด๋ผ๋ ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ๋ ์์ํฌ์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ตฌํ๋์ด ์์ด์ ๊ทธ๋๋ก ํ์ฉํ ์ ์์์ต๋๋ค.
์ ํ๋ก ํธ์๋๋ฅผ ์ ํํ์ จ๋์?
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์์ํ ์ด์ ๋ ์ ๋ง๋ค ๋ค๋ฅด๊ฒ ์ง๋ง, ๋ง์ ๊ฐ๋ฐ์๊ฐ ์ฌ์ฉ์์ ์ง์ ์ํตํ๋ ์ธํฐํ์ด์ค๋ฅผ ๋ง๋๋ ์ฆ๊ฑฐ์์ ์ธ๊ธํฉ๋๋ค. ์ ์ญ์ ๊ทธ๋ฐ ์ด์ ๋ก ์ด ๋ถ์ผ์ ๋น ์ ธ๋ค์์ต๋๋ค. ์ฑ์ ์ฌ์ฉํ๋ค ๋ณด๋ฉด, ์์ฐ์ค๋ฝ๊ฒ ์์ง์ด๋ UI์ ๊ทธ๋ ์ง ์์ UI์ ์ฐจ์ด๋ฅผ ๋๋ผ๊ฒ ๋ฉ๋๋ค. ๋ฉ๋ด๊ฐ ๋๋ฌด ๊ธฐ๊ณ์ ์ผ๋ก ์ด๋ฆฌ๋ฉด ์ด์ํ๊ณ , ์ ์ ํ ํ๋ ฅ์ผ๋ก ๋ถ๋๋ฝ๊ฒ ์์ง์ด๋ฉด ํ์ง์ด ๋๊ฒ ๋๊ปด์ง์ฃ . ์ด๋ฐ ์ฐจ์ด๊ฐ ์ฌ์ฉ์ ๊ฒฝํ์ ์ง์ ๊ฒฐ์ ํฉ๋๋ค.
๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์ ์ ์ฅ์ ์ ์๊ฒ ๋ ํ๋ก๋ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ํ์ํ๊ฒ ๋์์ต๋๋ค. ์๋ คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ณต์กํ ๋ฌผ๋ฆฌ ๊ณ์ฐ์ ๊ฐ์ถ๊ณ , ์ง๊ด์ ์ธ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํด ์ฐ๋ฆฌ๋ ์ฌ์ฉ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๋ณต์กํ ์ํ์ ๊ฐ๋ ์ ์ดํดํ์ง ์์๋ ์์ฐ์ค๋ฌ์ด ์์ง์์ ์ฝ๊ฒ ์ ์ฉํ ์ ์์ฃ .
์ด์ ๋ ํ๋ก์ ํธ๋ฅผ ํ ๋๋ง๋ค ์์ฐ์ค๋ฌ์ด ์ ๋๋ฉ์ด์ ์ ์ด๋ป๊ฒ ์ ์ฉํ ์ง ๊ณ ๋ฏผํ๊ฒ ๋ฉ๋๋ค. ์ฌ์ฉ์๋ ์ ๋๋ฉ์ด์ ์ ๊ธฐ์ ์ ๊ตฌํ ๋ฐฉ์์ ์์ํ์ง ๋ชปํฉ๋๋ค. ๋จ์ง "์ฌ์ฉํ๊ธฐ ์ข๋ค" ๋๋ "๋ญ๊ฐ ๋ถ์กฑํ๋ค"๋ผ๊ณ ๋๋ ๋ฟ์ด์ฃ . ํ์ง๋ง ๊ทธ ๋ฏธ๋ฌํ ์ฐจ์ด๊ฐ ๊ฒฐ๊ตญ ์ ํ์ ํ์ง๊ณผ ์ฌ์ฉ์ ๋ง์กฑ๋๋ฅผ ์ข์ฐํฉ๋๋ค.
์ฌ๋ฌ๋ถ์ด ์๊ณ ์๋ ์ ๋๋ฉ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด๋ค ๊ฒ์ด ์๋์?