Search

사라지는 DOM node에 out transition 효과 넣는 방법

태그
JavaScript
CSS
날짜
2024/05/08

CSS Transition

css 트랜지션 속성을 사용해, node의 상태에 따라서 애니메이션을 적용할 수 있습니다.
mdn에서는 CSS transition에 대해 아래와 같이 설명하고 있습니다.
CSS transitions는 여러분이 (명시적으로 목록을 작성해서) 어떤 속성을 움직이게 할지, (딜레이를 설정해서) 언제 애니메이션이 시작할지, (지속 시간을 설정해서) 트랜지션을 얼마나 지속할지, 그리고 (예를 들면, 선형이거나 초기 빠름, 종료 느림과 같은 타이밍 함수를 정의해서) 어떻게 트랜지션을 실행하는지 결정하게 합니다.

DOM 노출시 애니메이션 적용하기

자, 그럼 우리가 DOM상에 node를 하나 추가한다고 가정해보겠습니다.
이 때, scale이 0에서 1로 변화하는 효과를 2초에 걸쳐 노출한다고 가정해보면 아래와 같은 컴포넌트를 작성해 볼 수 있습니다.
// ScaleComponent.css .component { transform: scale(0); transition: transform 1s ease-in-out; } .component.visible { transform: scale(1); } // ============================ // React component import React, { useState, useEffect } from 'react'; import './ScaleComponent.css'; function ScaleComponent() { const [visible, setVisible] = useState(false); useEffect(() => { // 컴포넌트가 마운트된 후에 visible 상태를 true로 설정 setVisible(true); }, []); return ( <div className={`component ${visible ? 'visible' : ''}`}> 여기에 컨텐츠를 넣으세요. </div> ); } export default ScaleComponent;
JavaScript
복사
실제 렌더링된 컴포넌트는 아래와 같습니다

DOM에서 제거시 애니메이션 적용하기

여기서 문제가 하나 발생합니다.
우리가 React를 사용할 때, 어떤 node를 DOM상에서 제거(혹은 조건부로 노출)하기 위해 아래와 같은 문법을 자주 사용하곤 하는데요
아래 코드에 나타난 조건에 의해서 node는 DOM상에서 즉시 사라지곤 합니다
const App = () => { return <div> {isVisible && <SomeComponent/>} </div> }
JavaScript
복사
즉, node 자체가 사라지기 때문에, 어떤 css 스펙도 적용할 수 없다는 이야기가 됩니다 (2024 1월 기준)
하지만 framer-motion 라이브러리를 사용하면, DOM node에서 제거되는 트랜지션을 명세할 수 있습니다.

Framer는 어떻게 해결했나요?

framer-motion에서는 DOM node의 렌더링 상태 관리를 위해 <AnimatePresence/> 컴포넌트를 제공합니다
AnimatePresence는 children 컴포넌트들이 DOM node에서 사라질때 애니메이션을 표현할 수 있는 기능을 제공합니다
1.
컴포넌트가 unmount 예정이라면
2.
애니메이션 명세 렌더를 수행하고 → unmount 될 수 있도록 말이죠
기본적인 사용법은 아래와 같습니다
import { motion, AnimatePresence } from "framer-motion" export const MyComponent = ({ isVisible }) => ( <AnimatePresence> {isVisible && ( <motion.div initial={{ opacity: 0 }} // DOM 노드에 부착시 표시할 명세 animate={{ opacity: 1 }} // 노드 부착 직후에 표시할 명세 exit={{ opacity: 0 }} // unmount시 표시할 명세 /> )} </AnimatePresence> )
JavaScript
복사

해결 원리

사용자는 AnimatePresence에게 children을 넘겨주게 되는데요, 내부적으로 이 children 배열을 ref에 지정하여 들고있게 됩니다.
기존의 children element들을 ref 상태로 추적하고 이 값을 유지해 둔 뒤, 어느 순간에 새로운 children element들을 받게 되는데요.
새로운 element들을 바로 렌더하는 것이 아닌, 기존의 children들과 비교하여 사라진 노드를 찾게 됩니다.
이 diff들을 기반으로 exit props에 정의된 애니메이션을 수행하고, 해당 element들을 제거하게 됩니다.
해당 코드는 아래 링크에서 확인할 수 있습니다
index.tsx
AnimatePresence