React 19.2: Activity API 마스터하기와 INP 혁명
2026년 4월 현재, React 에코시스템은 Hooks 도입 이후 가장 중대한 변화를 맞이했습니다. React v19.2.4의 안정적인 릴리스와 함께, 프레임워크는 단순한 컴포넌트 렌더링을 넘어 지능적인 생명주기 관리의 영역으로 진입했습니다. React 컴파일러(React Forget)가 대중을 위한 메모이제이션을 자동화했다면, 이제 숙련된 엔지니어들은 더 강력한 프리미티브인 Activity API에 주목하고 있습니다.
이 가이드에서는 React 19.2가 컴포넌트의 "활동성(activity)"을 어떻게 관리하는지, 실험적인 "Offscreen"이라는 이름에서 안정적인 "Activity" API로 전환된 것이 왜 중요한지, 그리고 복잡한 애플리케이션에서 완벽에 가까운 INP(Interaction to Next Paint) 점수를 달성하기 위해 이 도구들을 어떻게 활용할 수 있는지 깊이 있게 다룹니다.
2026년의 풍경: React 19.2가 새로운 표준이 되다
2024년이 "Actions"의 해였고 2025년이 "컴파일러"가 주류가 된 해였다면, 2026년은 **리소스 효율성(Resource Efficiency)**의 해입니다. 이제 우리는 단순히 빠르게 렌더링하는 것을 넘어, 영리하게 렌더링하는 것을 목표로 합니다.
React 19.2.4는 다음과 같은 안정적인 기반을 제공합니다:
- React 컴파일러가 기본으로 작동하여, 코드베이스의 95%에서
useMemo와useCallback은 구시대의 유물이 되었습니다. - **서버 컴포넌트(RSC)**가 번들 크기에 영향을 주지 않고 데이터 페칭을 처리합니다.
- Activity API를 통해 전체 서브트리의 상태를 잃지 않고도 "일시 정지"할 수 있습니다.
Offscreen에서 Activity로: 무엇이 바뀌었나?
지난 몇 년간 커뮤니티는 <Offscreen />이라는 컴포넌트의 개발 과정을 지켜봐 왔습니다. React 19.2에서 이 기능은 <Activity /> API로 최종 확정되어 출시되었습니다. 이는 단순한 이름 변경이 아니라 멘탈 모델의 전환을 의미합니다.
"Offscreen"은 시각적인 제약(단순히 보이지 않음)을 암시합니다. 반면 "Activity"는 생명주기의 제약을 의미합니다. Active 상태의 컴포넌트는 사용자의 즉각적인 상호작용 루프에 참여하고 있는 컴포넌트입니다. Hidden 상태의 컴포넌트는 존재하며 내부 상태(스크롤 위치나 폼 입력 등)를 유지하지만, React 스케줄러에 의해 우선순위가 낮아진 상태를 말합니다.
기존 토글 방식의 문제점
React 19.2 이전에는 숨겨진 UI(예: 백그라운드 탭)를 처리하는 데 두 가지 결함이 있는 방식이 있었습니다:
- 조건부 렌더링 (
{show && <Component />}): 컴포넌트를 완전히 파괴합니다. 사용자가 다시 해당 탭으로 돌아오면 스크롤 위치, 작성 중이던 폼 데이터, 로컬 상태를 모두 잃게 됩니다. 모든 것이 다시 마운트되면서 "로딩 깜빡임"이 발생합니다. - CSS 숨기기 (
display: none): 상태는 유지되지만 성능 면에서는 악몽과 같습니다. React의 관점에서 컴포넌트 트리는 여전히 "활성(active)" 상태입니다. 백그라운드 탭이 무거운 리렌더링이나useEffect타이머를 실행하면 현재 보이는 UI와 CPU 자원을 두고 경쟁하게 되어, INP(Interaction to Next Paint) 점수를 떨어뜨립니다.
심층 분석: Activity API의 작동 원리
<Activity /> 컴포넌트는 이러한 "일시 정지" 상태를 선언적으로 관리할 수 있는 방법을 도입합니다.
import { Activity, useState } from 'react';
function Dashboard() {
const [activeTab, setActiveTab] = useState('overview');
return (
<main>
<Tabs onChange={setActiveTab} />
{/* Overview 탭은 선택되었을 때 항상 보입니다 */}
<Activity mode={activeTab === 'overview' ? 'visible' : 'hidden'}>
<OverviewTab />
</Activity>
{/* Analytics 탭은 무거울 수 있으므로 메모리에 유지하되 'hidden' 상태로 둡니다 */}
<Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
<AnalyticsTab />
</Activity>
</main>
);
}
1. 상태 유지 (State Preservation)
mode="hidden"일 때 DOM 요소는 유지되지만(hidden 속성 적용), 더 중요한 것은 파이버 트리(Fiber tree)가 그대로 유지된다는 점입니다. 사용자가 <AnalyticsTab /> 내부의 입력창에 "안녕하세요"라고 입력한 뒤 Overview로 전환했다가 돌아와도, 해당 텍스트는 그대로 남아 있습니다.
2. 이펙트 생명주기 관리 (Effect Lifecycle Management)
이것이 React 19.2의 "필살기"입니다. Activity 상태가 hidden으로 전환되면:
- React는 해당 서브트리에 있는 모든
useEffect훅의 **클린업 함수(cleanup functions)**를 자동으로 실행합니다. - 다시
visible상태가 되면, 이펙트가 **다시 동기화(re-sync)**되어 실행됩니다.
이를 통해 백그라운드 컴포넌트가 사용자가 보고 있지 않을 때 API를 폴링하거나 애니메이션 루프를 실행하는 것을 방지하여, 배터리와 CPU 자원을 절약합니다.
3. useEffectEvent의 강력함
상태 전환을 부드럽게 처리하기 위해, React 19.2 개발자들은 useEffectEvent 훅을 사용합니다. 이를 통해 불필요한 리렌더링을 유발하지 않으면서 Activity 상태 변화에 반응해야 하는 로직을 정의할 수 있습니다.
function ChatRoom() {
const onConnect = useEffectEvent(() => {
console.log("채팅 연결 재동기화 중...");
socket.connect();
});
useEffect(() => {
onConnect();
return () => socket.disconnect();
}, []); // Activity 모드가 변경될 때 자동으로 재동기화됩니다.
return <div className="chat">...</div>;
}
INP 혁명: 2026년에 성능이 중요한 이유
구글의 **INP(Interaction to Next Paint)**는 2024년에 핵심 웹 지표(Core Web Vital)가 되었지만, 2026년에는 SEO와 사용자 유지율을 결정하는 가장 중요한 지표가 되었습니다. Activity API는 INP를 개선하기 위한 궁극의 무기입니다.
React 스케줄러가 <Activity mode="hidden">을 발견하면, 해당 서브트리 전체를 **낮은 우선순위(Low Priority)**로 표시합니다. 숨겨진 트리에서 발생하는 모든 업데이트(예: 백그라운드 WebSocket 업데이트)는 현재 보이는 트리의 클릭이나 스크롤 같은 사용자의 상호작용을 결코 방해하지 않습니다.
벤치마킹 결과
탭당 50개 이상의 컴포넌트가 있는 복잡한 대시보드에서:
- Activity 미사용 시: 탭을 전환할 때 마운트/언마운트 오버헤드로 인해 250ms 이상의 INP 스파이크가 발생합니다.
- Activity 사용 시: DOM이 이미 존재하고 React가
visible상태 전환과 이펙트 재동기화만 수행하면 되므로, 전환이 거의 즉각적(16ms 미만)으로 이루어집니다.
비교표: 현대적 UI 토글 전략
| 기능 | 조건부 렌더링 | CSS display: none | Activity API (19.2) |
|---|---|---|---|
| 상태 유지 | 아니요 (초기화) | 예 | 예 |
| DOM 유지 | 아니요 | 예 | 예 |
| 이펙트 실행 | 클린업됨 | 백그라운드에서 실행 | 일시 정지/클린업됨 |
| 렌더링 우선순위 | 높음 (차단형) | 높음 (차단형) | 낮음 (지연 가능) |
| 이상적인 사례 | 모달, 단순 토글 | 작고 정적인 UI | 복잡한 탭, 프리렌더링 |
React 19.2 Activity를 위한 모범 사례
이 새로운 API를 최대한 활용하려면 다음과 같은 2026년형 패턴을 따르십시오:
"프리렌더링 비대화(Prerender Bloat)" 방지
10개의 탭을 메모리에 유지할 수 있다고 해서 반드시 그래야 하는 것은 아닙니다. 숨겨진 각 Activity는 메모리를 소비합니다. "최근 사용(LRU)" 캐시 패턴을 사용하여 가장 관련성이 높은 3~4개의 탭만 Activity 블록에 유지하고, 나머지는 조건부 렌더링을 사용하십시오.
서버 액션(Server Actions)과의 시너지
React 19.2의 Actions는 Activity와 완벽하게 연동됩니다. 백그라운드 탭이 useOptimistic을 통해 "낙관적 업데이트"를 수행하는 경우, Activity API는 해당 업데이트의 백그라운드 렌더링이 현재 포그라운드 작업에서 버벅거림을 유발하지 않도록 보장합니다.
Activity 전환 모니터링
새로운 useActivityStatus() 훅(19.2.2 도입)을 사용하여 컴포넌트가 백그라운드로 전환되는 시점을 감지하십시오. 이는 초안 데이터를 저장하거나 리소스를 많이 사용하는 WebGL 캔버스를 일시 정지하는 데 유용합니다.
function VideoPlayer() {
const status = useActivityStatus(); // 'visible' | 'hidden'
useEffect(() => {
if (status === 'hidden') {
videoRef.current.pause();
}
}, [status]);
return <video ref={videoRef} ... />;
}
FAQ: React 19.2에 대해 자주 묻는 질문
<Activity />가 <Suspense />를 대체하나요?
아니요. <Suspense />는 컴포넌트의 로딩 상태를 처리합니다. <Activity />는 이미 로드된 컴포넌트의 가시성과 우선순위를 처리합니다. 이 둘은 흔히 함께 사용됩니다. 예를 들어, Activity 내부에 데이터 페칭을 위한 Suspense 경계가 포함될 수 있습니다.
React 컴파일러가 Activity를 자동으로 처리하나요?
컴파일러는 컴포넌트가 최적화되도록 보장하지만, 컴포넌트가 언제 숨겨져야 하는지까지 결정하지는 않습니다. 개발자는 여전히 <Activity /> 컴포넌트를 사용하여 UI의 어느 부분이 현재 사용자의 초점이 아닌지 React에 알려주어야 합니다.
2026년 기준 React 19.2의 브라우저 지원 현황은 어떤가요?
2026년까지 모든 주요 에버그린 브라우저(Chrome 120+, Safari 18+, Firefox 125+)는 CSS content-visibility: hidden 및 현대적인 스케줄링 API를 포함하여 Activity API에 필요한 기본 프리미티브를 완전히 지원합니다.
결론
React 19.2는 프레임워크가 단순한 "뷰 라이브러리"를 넘어 정교한 **UI 오케스트레이터(UI Orchestrator)**로 진입하는 전환점이 되었습니다. Activity API를 마스터함으로써, 풍부한 기능을 제공하면서도 믿을 수 없을 만큼 성능이 뛰어난 애플리케이션을 구축할 수 있습니다.
2026년에 좋은 개발자와 위대한 개발자를 가르는 차이는, 사용자에게 보여주지 않는 리소스를 어떻게 관리하느냐에 달려 있습니다. 지금 바로 복잡한 인터페이스에 <Activity />를 통합하여, 현대 웹 시대에 사용자들이 기대하는 즉각적인 경험을 선사해 보십시오.
UnterGletscher 기술 블로그 — 2026년 웹 개발의 최전선에서 가장 날카로운 통찰력을 전해드립니다.