리액트를 사용하며 항상 고민이었던 부분, 혹시 여러분도 겪고 계신가요? 컴포넌트 성능 최적화를 위해 useMemo와 useCallback을 남발하고, useEffect의 의존성 배열 때문에 머리를 싸매던 날들. 그런 여러분께 반가운 소식이 있습니다! 2025년 10월 1일, React 팀이 React 19.2를 정식으로 출시했습니다.
이번 릴리즈는 지난 12월의 React 19, 6월의 19.1에 이어 올해만 세 번째 메이저 업데이트인데요. 과연 어떤 변화가 우리를 기다리고 있을까요? 지금부터 React 19.2의 신기능과 19.1과의 차이점을 낱낱이 파헤쳐보겠습니다.
React 19.2, 무엇이 달라졌나?
2025년은 React에게 정말 바쁜 한 해였습니다. 12월 React 19 정식 출시, 6월 19.1 업데이트, 그리고 이번 10월의 19.2까지. React 팀은 정말 쉴 새 없이 개발자들을 위한 새로운 기능을 선보였죠.
React 19의 핵심은 Server Components와 Actions API였습니다. 서버에서 컴포넌트를 렌더링해 초기 로딩 속도를 획기적으로 개선하고, 폼 제출이나 API 호출 같은 비동기 작업을 자동으로 처리해주는 기능이 추가됐죠. 그리고 React Compiler가 도입되면서 useMemo, useCallback 같은 최적화 훅을 수동으로 작성할 필요가 줄어들었습니다.
19.1에서는 owner stacks 기능이 개선되어 개발 단계에서 에러 추적이 더욱 쉬워졌고, useId의 기본 prefix가 :r:에서 «r»로 변경되었습니다.
그렇다면 19.2는 무엇이 다를까요?
🎯 React 19.2의 핵심 신기능
1. <Activity /> 컴포넌트 - 화면 전환의 혁명
React 19.2의 가장 눈에 띄는 기능은 바로 Activity 컴포넌트입니다. 이건 정말 게임 체인저예요.
기존에는 조건부 렌더링으로 컴포넌트를 보여주거나 숨겼죠?
// 기존 방식
{isVisible && <Page />}
이제는 Activity 컴포넌트로 더 똑똑하게 처리할 수 있습니다.
// React 19.2의 새로운 방식
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
Activity는 두 가지 모드를 지원합니다:
- visible: 자식 요소를 표시하고, Effect를 마운트하며, 업데이트를 정상적으로 처리합니다
- hidden: 자식 요소를 숨기고, Effect를 언마운트하며, React가 다른 작업이 없을 때까지 모든 업데이트를 지연시킵니다
이게 왜 중요할까요? 사용자가 다음에 방문할 가능성이 높은 페이지를 미리 렌더링해두면서도, 현재 화면의 성능에는 전혀 영향을 주지 않기 때문입니다. SPA(Single Page Application)의 페이지 전환 속도가 획기적으로 빨라지는 거죠!
실제 활용 예시:
function App() {
const [currentTab, setCurrentTab] = useState('home');
return (
<div>
<Activity mode={currentTab === 'home' ? 'visible' : 'hidden'}>
<HomePage />
</Activity>
<Activity mode={currentTab === 'profile' ? 'visible' : 'hidden'}>
<ProfilePage />
</Activity>
<Activity mode={currentTab === 'settings' ? 'visible' : 'hidden'}>
<SettingsPage />
</Activity>
</div>
);
}
이렇게 하면 탭을 전환할 때 페이지가 이미 렌더링되어 있어서 즉시 표시됩니다. 뒤로 가기를 했을 때도 입력 필드의 상태가 그대로 유지되죠!
2. useEffectEvent - useEffect의 고질적 문제 해결
useEffect를 사용하다 보면 의존성 배열 때문에 골치 아픈 경우가 많았죠. 특히 외부 시스템과 연동할 때 말이에요.
// 문제가 있는 코드
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
return () => connection.disconnect();
}, [roomId, theme]); // theme이 바뀔 때마다 재연결됨!
}
위 코드의 문제는 theme이 바뀔 때마다 채팅방이 재연결된다는 거예요. 정말 불필요한 동작이죠.
React 19.2의 해결책: useEffectEvent
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // theme은 의존성 배열에서 제거!
}
Effect Event는 항상 최신 props와 state를 "볼 수" 있으면서도 의존성 배열에 포함되지 않습니다. 린트 에러도 발생하지 않아요! 이 기능을 사용하려면 eslint-plugin-react-hooks@6.1.0 이상으로 업그레이드해야 합니다.
3. cacheSignal - Server Component의 캐시 수명 관리
Server Components를 사용하시는 분들을 위한 기능입니다. cache() 함수의 수명이 끝나는 시점을 알려주는 cacheSignal이 추가됐어요.
import {cache, cacheSignal} from 'react';
const dedupedFetch = cache(fetch);
async function Component() {
await dedupedFetch(url, { signal: cacheSignal() });
}
이렇게 하면 다음과 같은 경우에 작업을 정리하거나 중단할 수 있습니다:
- React가 렌더링을 성공적으로 완료했을 때
- 렌더링이 중단됐을 때
- 렌더링이 실패했을 때
불필요한 네트워크 요청을 줄여 성능을 최적화할 수 있죠!
4. Performance Tracks - Chrome DevTools 통합
개발자 도구에 React 전용 성능 트랙이 추가됐습니다. 이제 Chrome DevTools에서 React 앱의 성능을 훨씬 더 세밀하게 분석할 수 있어요.
Scheduler ⚛ Track
React가 다양한 우선순위(사용자 상호작용을 위한 "blocking", startTransition 내부의 "transition" 등)로 어떤 작업을 수행하는지 보여줍니다. 각 작업의 유형과 렌더링 시점을 시각적으로 확인할 수 있죠.
Components ⚛ Track
React가 작업 중인 컴포넌트 트리를 표시합니다. "Mount"(자식이나 Effect가 마운트될 때), "Blocked"(React 외부 작업으로 인해 렌더링이 차단될 때) 같은 라벨을 볼 수 있어요.
이 기능으로 컴포넌트가 언제 렌더링되고 Effect가 실행되는지, 그리고 작업 완료에 걸리는 시간을 파악해 성능 문제를 쉽게 찾아낼 수 있습니다!
🎨 React DOM의 새로운 기능
Partial Pre-rendering (부분 사전 렌더링)
React 19.2는 앱의 일부를 미리 렌더링하고 나중에 재개하는 기능을 추가했습니다. 이를 **"Partial Pre-rendering"**이라고 부르는데요.
// 미리 렌더링
const {prelude, postponed} = await prerender(<App />, {
signal: controller.signal,
});
// postponed 상태를 저장
await savePostponedState(postponed);
// prelude를 클라이언트나 CDN으로 전송
그다음 나중에 resume을 호출해서 SSR 스트림으로 재개할 수 있습니다:
const postponed = await getPostponedState(request);
const resumeStream = await resume(<App />, postponed);
// 스트림을 클라이언트로 전송
이 기능으로 정적인 부분은 CDN에서 빠르게 제공하고, 동적인 콘텐츠는 나중에 채워 넣을 수 있어요. 초기 로딩 속도가 엄청나게 빨라지는 거죠!
SSR: Node.js에서 Web Streams 지원
React 19.2는 Node.js 환경에서 Web Streams를 지원합니다:
- renderToReadableStream이 이제 Node.js에서 사용 가능
- prerender도 Node.js에서 사용 가능
- 새로운 resume API들(resume, resumeAndPrerender)도 Node.js 지원
다만 React 팀은 Node Streams이 Web Streams보다 훨씬 빠르고, Web Streams는 기본적으로 압축을 지원하지 않는다는 점을 언급했습니다. 따라서 프로덕션 환경에서는 Node Streams 사용을 권장하고 있어요.
🔧 주목할 만한 변경사항
Suspense Boundaries Batching
SSR 중 Suspense 경계가 표시되는 방식에 대한 동작 버그가 수정됐습니다.
이전(19.1 이전): Suspense 콘텐츠가 준비되는 즉시 개별적으로 표시됨 현재(19.2): Suspense 경계를 짧은 시간 동안 일괄 처리해 더 많은 콘텐츠를 함께 표시
이렇게 하면 View Transition을 사용할 때 더 큰 단위로 애니메이션을 실행할 수 있어서 사용자 경험이 훨씬 부드러워집니다!
useId prefix 업데이트
React 19.2에서는 useId의 기본 prefix를 다시 변경했습니다:
- React 19.0.0: :r:
- React 19.1.0: «r»
- React 19.2: _r_ 👈 최신
왜 계속 바꾸는 걸까요? View Transitions를 지원하기 위해서입니다. useId로 생성된 ID가 view-transition-name과 XML 1.0 이름에 유효해야 하기 때문이에요.
eslint-plugin-react-hooks v6
eslint-plugin-react-hooks@6.1.0도 함께 출시됐습니다. 기본 설정이 flat config로 변경됐고, React Compiler 기반의 새로운 규칙들을 옵트인 방식으로 사용할 수 있어요.
레거시 설정을 계속 사용하려면:
- extends: ['plugin:react-hooks/recommended']
+ extends: ['plugin:react-hooks/recommended-legacy']
📊 React 19, 19.1, 19.2 비교표
기능React 19React 19.1React 19.2
출시일 | 2024년 12월 | 2025년 6월 | 2025년 10월 |
주요 특징 | Server Components, Actions API, React Compiler | Owner stacks 개선 | Activity, useEffectEvent, Performance Tracks |
useId prefix | :r: | «r» | _r_ |
Partial Pre-rendering | ❌ | ❌ | ✅ |
useEffectEvent | ❌ | ❌ | ✅ |
cacheSignal | ❌ | ❌ | ✅ |
Performance Tracks | ❌ | ❌ | ✅ |
Node.js Web Streams | 제한적 | 제한적 | 완전 지원 |
🚀 React 19.2로 업그레이드해야 할까?
결론부터 말하면, 네! 특히 다음과 같은 경우라면 더욱 그렇습니다:
✅ SPA를 개발 중이고 페이지 전환 속도가 중요한 경우 - Activity 컴포넌트가 게임 체인저입니다
✅ useEffect의 의존성 배열 때문에 골치 아팠던 경우 - useEffectEvent가 해결책을 제시합니다
✅ 성능 최적화에 시간을 많이 쏟는 경우 - Performance Tracks로 병목 지점을 정확히 파악할 수 있습니다
✅ SSR/SSG를 사용하는 대규모 프로젝트 - Partial Pre-rendering으로 초기 로딩 속도를 획기적으로 개선할 수 있어요
🎓 마이그레이션 팁
React 19.2로 업그레이드하는 건 생각보다 간단합니다:
- npm 업데이트
npm install react@19.2.0 react-dom@19.2.0
- eslint 플러그인 업데이트
npm install eslint-plugin-react-hooks@6.1.0
- useEffectEvent 적용 검토
- useEffect의 의존성 배열 때문에 린트를 무시했던 곳들을 찾아보세요
- useEffectEvent로 리팩토링하면 코드가 훨씬 깔끔해집니다
- Activity 컴포넌트 활용 계획
- 조건부 렌더링으로 큰 컴포넌트를 숨기고 보이는 부분을 찾아보세요
- Activity로 전환하면 상태 유지와 성능 개선 두 마리 토끼를 잡을 수 있어요
- Performance Tracks 설정
- Chrome DevTools를 열고 Performance 탭으로 가세요
- 새로운 React 트랙들이 자동으로 표시됩니다
💡 실전 활용 시나리오
시나리오 1: 대시보드 앱
여러 탭이 있는 대시보드를 만든다고 가정해봅시다. 사용자는 주로 "홈"과 "통계" 탭을 오가며 사용하죠.
function Dashboard() {
const [activeTab, setActiveTab] = useState('home');
return (
<>
<TabMenu activeTab={activeTab} onTabChange={setActiveTab} />
<Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>
<HomeTab />
</Activity>
<Activity mode={activeTab === 'stats' ? 'visible' : 'hidden'}>
<StatsTab /> {/* 미리 렌더링되어 즉시 표시 */}
</Activity>
<Activity mode={activeTab === 'settings' ? 'visible' : 'hidden'}>
<SettingsTab />
</Activity>
</>
);
}
이렇게 하면 탭 전환이 거의 즉각적으로 느껴집니다!
시나리오 2: 실시간 채팅 앱
function ChatRoom({ roomId, userId, theme }) {
const [messages, setMessages] = useState([]);
// 테마가 바뀌어도 채팅방이 재연결되지 않음!
const onMessage = useEffectEvent((newMessage) => {
setMessages(prev => [...prev, newMessage]);
showNotification(newMessage, theme); // 최신 theme 사용
});
useEffect(() => {
const connection = createConnection(roomId, userId);
connection.on('message', onMessage);
connection.connect();
return () => connection.disconnect();
}, [roomId, userId]); // theme은 제외됨
return <MessageList messages={messages} theme={theme} />;
}
시나리오 3: E-commerce 상품 페이지
async function ProductPage({ productId }) {
const productData = await fetchProduct(productId, {
signal: cacheSignal() // 캐시 수명 관리
});
return (
<div>
<ProductDetails data={productData} />
<Activity mode="hidden">
<RelatedProducts /> {/* 백그라운드에서 로딩 */}
</Activity>
</div>
);
}
🎯 정리하며
React 19.2는 단순한 버그 수정 릴리즈가 아닙니다. 개발자 경험을 획기적으로 개선하는 실질적인 기능들이 가득하죠. Activity 컴포넌트로 페이지 전환을 부드럽게 만들고, useEffectEvent로 useEffect의 고질적 문제를 해결하며, Performance Tracks로 성능 병목을 정확히 파악할 수 있습니다.
특히 19.1에서 19.2로 넘어오면서 추가된 기능들은 정말 실용적입니다. Server Components를 사용하신다면 cacheSignal이 큰 도움이 될 거고, 복잡한 SPA를 개발하신다면 Activity는 필수예요.
'기술의기록' 카테고리의 다른 글
5분만에 이해하는 CSRF 공격 완벽 가이드 (1) | 2025.10.04 |
---|---|
다익스트라 알고리즘 완벽 정리 - 개념, 구현, 코딩테스트 활용법 (0) | 2025.10.02 |
2025년 최신! Claude Sonnet 4.5 vs Opus 4.1 완벽 비교 (0) | 2025.09.30 |
모바일 웹 개발자라면 꼭 알아야 할 CSS 새 뷰포트 단위 3가지 (dvh, svh, lvh 완벽 정리) (0) | 2025.09.30 |
CSS만으로 텍스트 로딩 애니메이션 만들기 - @property로 그라디언트 마법 부리기 (0) | 2025.09.25 |