외부 원문 링크
리액트 19.2.0의 새로운 기능
React 19.2 의 주인공은 <Activity>, useEffectEvent, cacheSignal, *부분 프리렌더링*. 컴포넌트 *생명주기/이펙트/SSR* 세 축에서 거친 모서리를 다듬은 마이너 릴리스다.
핵심 요약
주요 변경 사항을 도구별로:
// 1) <Activity> — 보이지 않을 때 상태 유지 + 작업 우선순위 다운
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
// 2) useEffectEvent — 이펙트 의존성에 안 넣고 최신 값 사용
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // 항상 최신 theme
});
useEffect(() => {
const c = createConnection(serverUrl, roomId);
c.on('connected', () => onConnected());
c.connect();
return () => c.disconnect();
}, [roomId]); // ✅ onConnected 추가 X
// 3) cacheSignal — 서버 컴포넌트의 비동기 작업 취소 신호
const dedupedFetch = cache(fetch);
async function Component() {
await dedupedFetch(url, { signal: cacheSignal() });
}
// 4) 부분 프리렌더링 — 정적 → CDN, 동적 → resume
const { prelude, postponed } = await prerender(<App />, { signal });
// ... CDN 으로 prelude, 나중에
const resumeStream = await resume(<App />, postponed);
그 외: SSR Suspense 일괄 처리, Node.js SSR 의 웹 스트림 지원, eslint-plugin-react-hooks@6.1.0 (Flat config 기본), useId prefix 변경(«r» → «r» → r).
19.2 의 변화는 "새 기능 추가" 가 아니라 "기존 모델의 미해결 통증을 정리" 다. (1) <Activity> — 조건부 렌더의 상태 보존 통증, (2) useEffectEvent — 이펙트 의존성의 최신 값 캡처 통증, (3) 부분 프리렌더링 — SSR 의 정적/동적 분리 통증. 즉 'React 의 모서리를 갈았다' 가 멘탈 모델이다.
면접에서 React 19 가 화제가 되면 보통 Server Components/Actions/use 로 흐른다. 19.2 는 그보다 자주 쓸 작은 도구들 을 추가하기 때문에 다음을 분리해 답할 수 있어야 한다.
Activity: 화면 전환 시 언마운트 vs 상태 유지 의 표준 해법useEffectEvent: "의존성에 안 넣고 싶지만 최신 값은 필요" 의 정식 해법 —useRef패턴을 대체cacheSignal: 서버 컴포넌트의 비동기 정리(cleanup) 가 표준화- 부분 프리렌더링: 정적 셀 → CDN 즉시, 동적 부분 → 재개 의 2단 패턴
이걸 언제 쓰는가 까지 답할 수 있으면 시니어 신호다.
학습 포인트
면접 답변으로 연결할 학습 포인트입니다.
`<Activity>` — 조건부 렌더의 표준 진화
기존 패턴 {isVisible && <Page />} 는 언마운트 라 상태(스크롤/입력값)가 사라진다. 보존하려면 CSS display:none + 수동 aria-hidden 같은 헛스러운 패턴을 쓰던 게 현실이었다.
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
동작:
hidden— 자식 숨김, 이펙트 언마운트, 업데이트는 다른 작업이 끝날 때까지 연기visible— 자식 표시, 이펙트 마운트, 정상 우선순위
쓸 곳: 탭/모달/사이드 패널 의 백그라운드, 다음 라우트 프리로드, 폼 입력값 보존. 즉 언마운트와 상태 보존 사이의 빈 자리를 표준화한 것.
"display:none 으로 충분하지 않냐" 라고 보는 것. CSS 는 우선순위 다운/업데이트 연기 같은 React 스케줄러 통합이 안 된다 — 큰 차이다.
`useEffectEvent` — 의존성 지옥의 정식 출구
useEffect 안에서 최신 props/state 를 쓰면서 그것 때문에 다시 실행되지 않게 하는 것은 그동안 useRef 패턴으로 우회해왔다. 19.2 는 이를 이벤트 함수 로 정식화한다.
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // 최신 theme
});
useEffect(() => {
const c = createConnection(serverUrl, roomId);
c.on('connected', () => onConnected());
c.connect();
return () => c.disconnect();
}, [roomId]); // theme 변경에도 재연결 X
핵심 의미: "이펙트는 동기화 목적, 이벤트는 즉시 액션" 의 경계가 코드로 표현 된다. lint(react-hooks@6.1.0) 가 잘못된 사용을 잡아준다.
useEffectEvent 를 모든 콜백에 쓰는 것. 이벤트 함수는 이펙트 안에서만 호출 해야 한다 — 외부에서 호출하면 의도가 깨진다.
부분 프리렌더링 — *정적/동적* 의 2단 배포
프리렌더링은 "정적 셸 즉시 + 동적 채움 재개" 의 표준 API 가 됐다.
// 1) 빌드/배포 — 정적 셸
const { prelude, postponed } = await prerender(<App />, {
signal: controller.signal,
});
await savePostponedState(postponed);
// prelude 는 CDN 으로
// 2) 요청 시 — 재개
const postponed = await getPostponedState(req);
const stream = await resume(<App />, postponed); // SSR
// 또는 SSG 완전 정적
const { prelude } = await resumeAndPrerender(<App />, postponed);
장점:
- TTFB 빨라짐 (정적 셸 CDN 즉답)
- 로직 분리 (정적/동적 경로가 명확)
- Suspense 일괄 처리 와 결합해 스트리밍 깜빡임 감소
단점: 인프라가 postponed 상태 저장소 를 가져야 한다 — 운영 복잡도 상승.
ISR/SSR/SSG 와 같은 것 으로 보는 것. 부분 프리렌더링은 한 페이지 안에서 정적/동적이 공존 — 차원이 다르다.
읽는 순서
- 1이론
React 19.2 릴리스 노트와 useEffectEvent / Activity / 부분 프리렌더링 RFC 를 한 시간 안에 훑고, 각 기능의 기존 우회 패턴 을 옆에 적어 비교 표로 만드세요.
- 2구현
탭 UI 하나를 골라
<Activity>로 마이그레이션. 스크롤/입력값 보존을 React DevTools 로 검증하세요. 그리고useRef우회 한 곳을useEffectEvent로 옮기고 lint 경고 사라짐을 확인. - 3실무
프로젝트에
eslint-plugin-react-hooks@6.1.0으로 업그레이드. Flat config 기본을 켜고 recommended-legacy 가 필요한 코드부터 점진 이행. - 4설명
팀에 10분 발표 — '19.2 의 4가지 작은 도구가 우리 코드의 어떤 우회 패턴을 정리하는가'. 각 도구마다 전/후 코드 비교 슬라이드 한 장씩.
면접 연결 질문
[감점 답변] '편하다'. [좋은 답변] (1) 상태 보존: 언마운트되지 않으므로 스크롤/입력값 유지. (2) 스케줄러 통합: hidden 시 업데이트가 다른 작업 끝까지 연기 되어 백그라운드 비용을 자동으로 낮춤. (3) 이펙트 라이프사이클: 보일 때만 마운트되어 리소스/구독 관리 정확도 향상. CSS 는 1번/3번을 동시에 해결할 수 없다.
[감점 답변] '그냥 쓰면 됨'. [좋은 답변] (1) 이펙트 안에서만 호출 — 컴포넌트 본문/이벤트 핸들러에서 직접 호출하면 안 됨. (2) 의존성에 넣지 말 것 — 넣으면 의도가 깨진다. lint 규칙이 위반을 잡아주므로 eslint-plugin-react-hooks@6.1.0 으로 업그레이드 필수.
[감점 답변] '더 빠름'. [좋은 답변] 두 측면. (1) 이득: 정적 셸이 CDN 캐시되어 TTFB 매우 짧음, 동적 부분만 origin 부담. (2) 비용: postponed 상태를 저장/조회 할 인프라가 필요하고, 빌드/런타임 양쪽 코드를 같은 트리에서 관리. 작은 프로젝트엔 과한 도입, 큰 트래픽엔 origin CPU 절감 으로 ROI 양호.
자기 점검
"모든 화면에 적용" 이라는 과도한 일반화. 전환이 잦고 상태 보존 가치가 큰 곳 에 한정해야 의미가 있다.
useRef 패턴이 동등하다 는 인식. 실제로는 lint 가 의도 위반을 못 잡고, 코드 의도가 동기화/이벤트 로 분리되지 않는다.
"디자인 결정" 이라는 인식. 실제로는 View Transitions / view-transition-name 같은 표준이 XML 1.0 호환 식별자 를 요구해 발생한 호환성 강제 다.