Activity 컴포넌트
작성일:2025.10.09|조회수:1

리액트로 UI를 구성하다 보면, 특정 컴포넌트를 조건부로 렌더링해야 하는 상황이 자주 발생한다. 일반적으로는 삼항 연산자 등을 활용해서 컴포넌트를 렌더링하는데, 내 경우에는 이런 조건 렌더링을 반복적으로 작성하는 게 싫어
이러한 상태 유실 문제를 피하기 위해 많은 개발자들이 컴포넌트를 언마운트하는 대신 CSS의 display: none 속성으로 잠시 숨기는 방법을 선택하기도 한다. 이 방법은 컴포넌트가 계속 마운트된 상태를 유지하므로 상태를 완벽하게 보존할 수 있다는 확실한 장점이 있다. 그러나 이는 또 다른 문제를 야기하는데, 컴포넌트가 화면에 보이지 않을 뿐 백그라운드에서는 여전히 '살아있는' 상태로 모든 Effect를 계속 실행한다는 것이다. 내부의 useEffect는 계속해서 외부 API를 주기적으로 호출(polling)하거나 웹소켓 연결을 유지하는 등의 작업을 멈추지 않으며, 이는 결국 사용자가 보지도 않는 기능에 소중한 리소스를 낭비하는 결과를 낳게 된다.
바로 이 문제를 해결하기 위해 React 19.2 버전에서 Activity라는 새로운 기능이 도입되었다. Activity는 컴포넌트를 완전히 언마운트하지 않고도 비활성화된 상태로 전환할 수 있게 해주는 개념이다. 즉, DOM과 상태는 그대로 유지하되, 그 안의 Effect와 이벤트 핸들러는 일시적으로 중단된다. 쉽게 말해 Activity는 display: none과 unmount의 중간 지점에 있는 기능이다. 컴포넌트를 시각적으로 감추면서도 상태를 그대로 보존하고, 동시에 불필요한 Effect 실행을 막아 리소스를 절약할 수 있다.
아래의 예시는 Activity를 사용하여 탭 전환 시 상태를 보존하면서도, 보이지 않는 탭의 Effect는 자동으로 중단시키는 코드이다.
import { useState, useEffect } from 'react';
import { Activity } from 'react';
function ChatTab() {
useEffect(() => {
console.log('ChatTab active');
const interval = setInterval(() => {
console.log('🔵 Fetching messages...');
}, 2000);
return () => {
console.log('ChatTab paused');
clearInterval(interval);
};
}, []);
return <div>💬 Chat Messages</div>;
}
function ProfileTab() {
useEffect(() => {
console.log('ProfileTab active');
return () => console.log('ProfileTab paused');
}, []);
return <div>👤 User Profile</div>;
}
export default function TabView() {
const [activeTab, setActiveTab] = useState('chat');
return (
<div>
<button onClick={() => setActiveTab('chat')}>Chat</button>
<button onClick={() => setActiveTab('profile')}>Profile</button>
<Activity mode={activeTab === 'chat' ? 'visible' : 'hidden'}>
<ChatTab />
</Activity>
<Activity mode={activeTab === 'profile' ? 'visible' : 'hidden'}>
<ProfileTab />
</Activity>
</div>
);
}이 코드를 실행하면, 사용자가 탭을 전환할 때 ChatTab과 ProfileTab의 useEffect가 자동으로 중단되거나 재개되는 것을 확인할 수 있다. React는 비활성화된 Activity를 DOM 트리 안에 그대로 유지하지만, 그 내부의 Effect와 이벤트 핸들러는 모두 일시 정지된 상태로 만든다. 다시 활성화되면 React는 기존 상태를 그대로 복원하고 필요한 Effect만 재실행한다.
이 구조 덕분에 Activity는 상태 보존과 부수 효과 제어, 그리고 성능 최적화까지 모두 만족시킬 수 있다. 기존의 조건부 렌더링이나 display: none 접근 방식이 가진 한계를 뛰어넘는 새로운 상태 관리 모델이라고 할 수 있다.
더 읽어보기
2026.03.01
렌더링 전략 정리
리액트와 Next.js에서 렌더링 전략은 단순한 옵션 선택이 아니다. 이는 서비스의 초기 로딩 속도, 서버 비용, 캐싱 전략, SEO 노출, 개발 복잡도까지 동시에 좌우하는 아키텍처 결정이다. 프로젝트 규모가 커질수록 “어디에서 HTML을 생성하는가”, “언제 자바스크립트를 실행하는가”…
2026.02.22
View Transition API
웹 애플리케이션에서 전환 품질은 기능 완성도와 동등한 수준으로 중요하다. 사용자가 목록에서 항목을 선택해 상세 화면으로 이동할 때, 화면이 자연스럽게 이어지면 서비스는 빠르고 안정적으로 느껴진다. 반대로 동일한 기능이라도 전환이 끊기면 체감 성능과 신뢰도는 동시에 하락한다. 전환은 부가…
2025.10.08
useEffectEvent
React를 사용하면서 useEffect 안에서 상태를 참조할 때 의외로 자주 겪는 문제가 있다. 바로 stale closure 문제다. 예를 들어 어떤 값이 변경되었는데, useEffect 내부의 콜백에서는 여전히 이전 값을 읽고 있는 현상이다. 이는 React의 클로저 구조상 자연스…
2025.05.31
모달과 팝오버, 그리고 앵커 포지셔닝
모달과 팝오버를 칼로 베듯 명확히 구분해본 적은 없었다. 그저 화면 한가운데에 떠서 다른 작업을 막으면 모달, 특정 버튼을 눌렀을 때 앵커를 기준으로 나타나면 팝오버라는 정도로 느슨하게 생각해왔다. 하지만 두 개념은 단순한 위치나 동작의 차이를 넘어, 그 목적과 사용 방식에서 뚜렷한 차…
2026.06.07
AI 에이전트의 비밀값을 macOS Keychain에 맡기기
AI 에이전트나 스킬을 만들다보면 비밀값을 어떻게 관리하면 좋을지 하는 생각을 자주 하게 된다. API를 호출하려면 API Key가 필요하고, 특정 기능을 자동화하는 과정에서 아이디와 비밀번호가 필요할 수도 있다. 그런데 그 값을 프롬프트에 박아버리면 대화 기록에 남고, 명령어 인자로…
2026.06.01
React Server Components를 위한 컴포넌트 아키텍처
이 포스트는 Vercel의 Next.js 팀 소속 개발자 Aurora Scharff가 자신의 블로그에 올린 Component Architecture for React Server Components 게시글을 번역한 것이다. 번역하는 과정에서 다소 의역이 있을 수 있으며, 일부 번역에는…
2026.05.26
차트는 멈췄는데 윈도우가 움직인다
상황 어느날 서비스를 살펴보시던 팀장님께서 이런 말씀을 slack에 남기셨다. 진호님, 예측 차트에서 zoom을 계속하면 어느 순간 라인 차트가 아니라 단일 스캐터 차트처럼 보이는 데, 이거 수정하면 좋을 거 같아요. 어느정도 zoom을 하면 그 이후로는 zoom이 안 되도록 할 수 없…
2026.05.21
피자가게로 이해하는 디자인 패턴
에이든 피자는 처음부터 복잡한 시스템을 만들 생각이 없었다. 처음에는 메뉴 몇 개만 만들면 됐다. 그런데 손님은 커스텀 주문을 넣기 시작했고, 주방은 상태를 나눠야 했고, 결제와 배달앱과 알림이 하나씩 붙었다. 코드도 가게를 닮는다. 장사가 잘될수록 이상하게 더 쉽게 망가진다. 디자인…
댓글
댓글을 불러오는 중...