React 19.3, 뭐가 달라졌나요?

2026년 9월 9일, React 팀이 React 19.3을 npm에 정식 배포했습니다. 작년에 experimental로 공개했던 View TransitionsFragment Refs가 드디어 stable로 승격된 게 이번 릴리즈의 핵심이에요.

실무에서 체감할 수 있는 변화 위주로 정리해봤습니다. 특히 애니메이션과 DOM 제어가 필요했던 프로젝트라면 이번 버전이 꽤 반가울 거예요.

근거자료: React 19.3 공식 릴리즈 노트

이번 릴리즈 한 줄 요약

  • View Transitions stable: 브라우저 View Transition API를 React 컴포넌트로 감싸서 쓸 수 있음
  • Fragment Refs stable: wrapper div 없이 여러 DOM 노드를 그룹으로 제어
  • browser() API 신규: SSR 환경에서 브라우저 전용 컴포넌트를 우아하게 opt-out
  • Trusted Types 지원: CSP require-trusted-types-for 'script' 환경에서 XSS 방어
  • Server Components에서 Context 직접 렌더링 가능

Developer reviewing React 19.3 release notes with View Transition API code on laptop screen Developer Related Image

핵심 기능 1: View Transitions 제대로 쓰기

<ViewTransition> 컴포넌트로 UI 일부를 감싸면, 해당 영역이 enter / exit / update / share 상태에 따라 자동으로 애니메이션돼요.

import { ViewTransition, useState, startTransition } from 'react';
import { Video } from './Video';
import videos from './data';

export default function Component() {
  const [showItem, setShowItem] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          // Transition으로 표시해야 애니메이션이 트리거됩니다
          startTransition(() => {
            setShowItem((prev) => !prev);
          });
        }}
      >
        {showItem ? '➖' : '➕'}
      </button>
      {showItem && (
        <ViewTransition>
          <Video video={videos[0]} />
        </ViewTransition>
      )}
    </>
  );
}

주의: Transition으로 감싸야 애니메이션이 돕니다

startTransition, useDeferredValue로 발생한 업데이트만 View Transition이 트리거돼요. 일반 setState는 즉시 반영되는 urgent 업데이트로 취급되어 애니메이션이 걸리지 않습니다. 이 부분 헷갈리면 "왜 애니메이션 안 되지?" 하고 한참 삽질하게 돼요.

같은 상태 변경인데 다른 애니메이션을 쓰고 싶다면?

addTransitionType을 쓰면 됩니다. 예를 들어 캐러셀에서 다음 슬라이드로 갈 때와 이전 슬라이드로 갈 때 애니메이션 방향을 다르게 줄 수 있어요.

function nextSlide() {
  startTransition(() => {
    addTransitionType('next');
    setCurrentSlide(c => c + 1);
  });
}

function previousSlide() {
  startTransition(() => {
    addTransitionType('previous');
    setCurrentSlide(c => c - 1);
  });
}

CSS에서는 :active-view-transition-type(next) 같은 선택자로 스코프를 잡을 수 있습니다.

Suspense와 조합할 때의 황금률

View Transition과 Suspense를 같이 쓸 때 자주 하는 실수가 있어요. 캐시된 UI까지 매번 애니메이션이 걸려서 오히려 느리게 느껴지는 경우입니다.

React 팀이 제시하는 원칙은 세 가지예요:

  1. Fallback은 애니메이션 없이 즉시 나타나야 함
  2. Fallback → 최종 콘텐츠 전환은 애니메이션 O
  3. suspend되지 않는 자식은 애니메이션 없이 즉시 표시

이 원칙을 지키려면 <ViewTransition>enter/exit를 비활성화하고 update만 살려두는 식으로 조정하면 됩니다.

<ViewTransition update="auto" enter="none" exit="none">
  <Suspense fallback={<VideoPlaceholder />}>
    <LazyVideo />
  </Suspense>
</ViewTransition>

핵심 기능 2: Fragment Refs

wrapper div 없이 여러 형제 DOM 노드를 그룹으로 다뤄야 할 때가 있죠. 특히 외부 라이브러리 컴포넌트ref를 안 넘겨주면 방법이 없었는데, Fragment Refs가 이 문제를 해결합니다.

import { useRef, useEffect } from 'react';

function Component({ posts }) {
  const fragmentRef = useRef(null);

  useEffect(() => {
    const fragmentInstance = fragmentRef.current;
    // Fragment의 첫 번째 레벨 자식들에 대해 포커스 이동
    fragmentInstance.focus();
  }, []);

  return (
    <Fragment ref={fragmentRef}>
      {posts.map(post => (
        <article key={post.id}>{post.title}</article>
      ))}
    </Fragment>
  );
}

FragmentInstance가 제공하는 메서드는 다음과 같아요:

  • addEventListener / removeEventListener / dispatchEvent: 첫 번째 레벨 자식 이벤트 관리
  • focus / focusLast / blur: depth-first 순회하며 포커스 이동
  • observeUsing / unobserveUsing: IntersectionObserver, ResizeObserver 연결
  • getClientRects / getRootNode / compareDocumentPosition / scrollIntoView: 측정 및 스크롤

핵심 기능 3: browser() API로 SSR opt-out

서버에서는 의미 있는 UI를 만들 수 없는 컴포넌트(예: localStorage나 로컬 타임존 의존)를 우아하게 SSR에서 제외시킬 수 있습니다.

기존 방식은 이랬어요:

// 기존: mounted 상태로 우회
function Component() {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  // ...
}

React 19.3에서는 이렇게 씁니다:

import { use } from 'react';
import { browser } from 'react-dom';

function TimeZone() {
  use(browser()); // 서버에서는 Suspense 트리거, 클라이언트에서는 통과
  const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
  return <span>{timeZone}</span>;
}

조건문 안에서도 호출 가능하기 때문에, initialData 유무에 따라 SSR을 선택적으로 opt-out 하는 패턴도 깔끔하게 짤 수 있습니다.

function useBrowserQuery(query, options) {
  if (options.initialData === undefined) {
    use(browser()); // 초기 데이터 없으면 브라우저에서만 실행
  }
  return useQuery(query, options);
}

그 외 주목할 변경사항

  • Trusted Types 지원: CSP에서 require-trusted-types-for 'script'를 강제하는 환경에서 React가 더 이상 값을 강제로 문자열로 변환하지 않음 → 정책이 정상 동작
  • Server Components에서 Context 직접 렌더링: 별도 Provider wrapper 컴포넌트 없이 'use client' 모듈의 Context를 직접 import해서 렌더 가능
  • Render Transitions 독립 처리: 느린 Transition이 다른 Transition을 막지 않도록 개선
  • onFullscreenChange / onFullscreenError 이벤트 지원
  • fetchPriority 모듈 리소스 지원
  • form state → action state 용어 정리 (useActionState 에러 메시지)

React component tree diagram showing ViewTransition wrapper animating enter exit share states

버전별 주요 기능 비교

기능이전 버전React 19.3실무 영향
View Transitionsexperimentalstable프로덕션 도입 가능, Suspense 연동
Fragment Refs없음stablewrapper div 없이 DOM 그룹 제어
browser() API없음 (수동 우회)신규SSR opt-out 코드 대폭 단순화
Trusted Types문자열 강제 변환통과CSP strict 환경에서 XSS 방어 정상화
Server Component ContextProvider wrapper 필요직접 렌더보일러플레이트 제거
Render Transitions단일 렌더로 얽힘독립 처리느린 Transition이 다른 것 방해 안 함

이 기술의 한계와 주의사항

솔직히 말하면, 만능은 아닙니다.

1. View Transitions는 아직 DOM 전용

공식 문서에도 명시되어 있듯이, React Native와 다른 플랫폼 지원은 아직 진행 중입니다. RN 프로젝트라면 이번 릴리즈의 애니메이션 개선은 해당 사항이 없어요.

2. 브라우저 View Transition API 의존

결국 기반은 브라우저의 View Transition API입니다. 지원하지 않는 브라우저에서는 애니메이션이 그냥 안 걸려요(폴백이 있는 건 다행).

3. Suspense + 애니메이션은 남용 금물

React 팀도 명시적으로 "sparingly (아껴서) 쓰라"고 권고합니다. 캐시된 UI에까지 애니메이션을 걸면 오히려 앱이 느리게 느껴질 수 있어요.

4. Fragment Refs는 제한적 API

전체 DOM API를 다 노출하는 게 아니라 **"commonly used DOM methods"**만 제공합니다. querySelector 같은 건 없으니 필요하면 기존 ref를 병행해야 해요.

한국 개발 생태계에서의 적용 맥락

국내 SI / 스타트업 환경에서 특히 유의할 점이 몇 가지 있습니다.

  • 레거시 브라우저 지원: 관공서·금융권 SI 프로젝트는 여전히 구형 브라우저 지원 요구가 많아요. View Transitions는 폴백이 있지만, QA 단계에서 "왜 애니메이션이 안 되냐"는 이슈가 반드시 나옵니다. 초기에 브라우저 매트릭스를 공유해두세요.
  • Next.js App Router와의 궁합: Next.js 15+ 환경에서 Server Component에서 Context를 직접 렌더하는 기능은 매우 반가운 변화입니다. 기존에 'use client' Provider wrapper로 도배되던 레이아웃 코드가 확 줄어들어요.
  • browser() API와 dynamic import: 기존에는 next/dynamicssr: false로 우회하던 패턴을 React 레벨에서 처리할 수 있게 됐습니다. 다만 프레임워크별 컨벤션과 충돌하지 않는지 확인 필요해요.
  • Trusted Types: 국내에서 Trusted Types를 실제로 강제하는 서비스는 아직 드물지만, 금융·공공 보안 감리 대응 시 CSP 강화 요구가 늘고 있어 미리 알아두면 좋습니다.

다음 단계 학습 방향

  1. 공식 문서 정독: ViewTransition, addTransitionType, FragmentInstance, browser() 각 API의 공식 문서를 먼저 훑어보세요.
  2. 실습 프로젝트: 작은 토이 프로젝트에 View Transitions를 얹어보면서 enter/exit/share 동작을 직접 확인하는 게 가장 빠릅니다.
  3. Suspense 조합 패턴 익히기: update만 활성화하는 패턴, fallback 즉시 표시 원칙 등을 코드로 체화하세요.
  4. 마이그레이션 계획: 기존 mounted 상태 우회 코드를 browser()로 리팩터링할 수 있는지 점검해보세요.

Frontend engineer testing Fragment Refs focus management in React 19.3 on desktop setup Technical Structure Concept

정리하며

React 19.3은 화려한 신기능보다 **"실험 단계였던 것을 실무에서 쓸 수 있게 다듬은 릴리즈"**라고 보시면 됩니다. View Transitions와 Fragment Refs가 stable이 된 건 분명 반가운 소식이고, browser() API와 Trusted Types 지원은 SSR 환경과 보안 요구사항이 까다로운 프로젝트에서 체감이 클 거예요.

다만 View Transitions는 RN 미지원, 브라우저 API 의존, Suspense 조합 시 남용 금지라는 제약을 꼭 기억하세요. "애니메이션 다 넣으면 좋겠지"라는 접근은 오히려 UX를 해칩니다.

함께 보면 좋은 글

업그레이드는 항상 작은 프로젝트에서 먼저 검증하고, 프로덕션은 브라우저 매트릭스와 QA 시나리오를 정비한 뒤에 진행하시는 걸 추천합니다.

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.