본문 바로가기
IT/React & Next.js

[React] Activity 컴포넌트

by 저당단 2026. 4. 2.

 

Activity는 useEffectEvent처럼 React 19에서 추가되었다.

 

이전에는 사이드바를 접을 때 아래와 같은 코드를 주로 사용했다.

function TabContent({ active }) {
  return (
    {
      active && <HeavyComponent />
    }
  );
}

active가 false가 되었을 땐 언마운트시키는 방법이다.

그러나 이러면 다시 마운트시켰을 때 내부에서 사용되던 모든 state값들이 초기화되어 버린다.

 

상태를 보존하기 위해선 CSS 속성에서 display: none을 주면 되는데,

해당 컴포넌트는 보이지 않을 뿐이지 여전히 활성 상태이다.

때문에 내부의 useEffect 같은 로직은 계속 돌아간다.

이는 불필요한 리소스 낭비가 될 수 있다.

 

이때 Activitiy를 이용한다.

function TabContent({ active }) {
  return (
    <Activity mode={active ? 'visible' : 'hidden'}>
      <HeavyComponent />
    </Activity>
  );
}

 

컴포넌트를 언마운트시키지 않고, 비활성 상태로 만든다.

말그대로 일시정지라 다시 활성화되면 input 입력값이나 스크롤 위치까지 다 동일하게 남아있다.

그러나 아무래도 언마운트보다는 메모리를 먹기 때문에 저사양 기기에서는 성능 문제가 생길 수도...

 

 

원리

React는 16버전부터 Fiber라는 아키텍처를 쓰고 있다.

거대한 렌더링 작업을 Fiber 노드라는 작은 단위로 쪼개는 방법이다.

그 작업마다 Lane이라는 우선순위를 매기는데, 여기에 따라 중요한 것부터 먼저 렌더링시킨다.

작업을 수행하다가 도중에 사용자의 입력이 들어오면 일단 작업을 멈출 수도 있다.

그 때 도입된 게 Pause와 Resume라는 개념이다. 즉 작업을 멈췄다가 다시 시작하고, 폐기하기도 한다.

 

결국 Activity 컴포넌트가 mode=hidden이면

내부 요소들의 Lane을 최하로 낮춘 뒤 Pause를 시켜놓는 것이다.

이때 내부 useEffect의 cleanup을 실행해서 작업을 중단시킨다.

즉 컴포넌트가 언마운트될 때 실행될 코드가 실행된다는 것이다.

 

 

(+) Fiber에서 렌더링 프로세스는 두 단계로 나뉘는데,

1. Render Phase: 새로운 상태를 반영한 트리를 비동기로 만듦.

2. Commit Phase: 그 결과를 가지고 실제 DOM에 동기로 빠르게 반영.

 

 

사이드바 애니메이션을 쓰려면?

Activity의 hidden, visible 속성이 바뀔 때 transform 애니메이션을 쓸 수 있을까 했지만 그건 또 아니었다.

내부적으로 CSS의 display: none을 쓰기 때문.

 

사이드바를 접는 애니메이션을 만들고 싶으면

1. 먼저 transform을 시켜서 접히는 애니메이션을 보여줌

2. setTimeout으로 몇초 뒤(이건 잘 안씀),

    혹은 사이드바의 onTransitionEnd 이벤트 이용해서 언마운트 처리(정석적)

    혹은 requestAnimationFrame 안에서 언마운트 처리(좀 복잡한데 세밀한 작업 가능)

// onTransitionEnd 사용
function Sidebar({ isOpen }) {
  const [shouldRender, setShouldRender] = useState(isOpen);

  const handleTransitionEnd = () => {
    if (!isOpen) setShouldRender(false); // 닫기 애니메이션이 끝나면 언마운트
  };

  // 언마운트
  if (!shouldRender) return null;

  return (
    <div
      onTransitionEnd={handleTransitionEnd}
      className={`sidebar ${isOpen ? 'open' : 'close'}`}
      style={{
        transition: 'transform 0.3s ease-in-out',
        transform: isOpen ? 'translateX(0)' : 'translateX(-100%)'
      }}
    >
      사이드바 컨텐츠
    </div>
  );
}

 

이 과정에서 언마운트 처리 대신 Activity 에 hidden 속성을 준다는 것만 달라진다.

이게 귀찮으면 Framer Motion 라이브러리를 사용하게 되는 것.