Menu

React 19: 새 기능, 제거된 API, 업그레이드 방법

React 19는 액션, useActionState, useOptimistic, use, prop으로서의 ref, 문서 메타데이터를 추가했고, 19.2는 Activity와 useEffectEvent를 추가했습니다. 각 기능을 실행해 보고, 무엇이 제거되었는지, 어떻게 업그레이드하는지 살펴봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

React 19는 2024년 12월에 출시된 React의 메이저 버전입니다. 액션(대기 상태, 오류, 폼을 관리하는 async 함수), useActionState, useOptimistic, use 훅, 일반 prop으로서의 ref, 그리고 React가 문서 head로 옮겨 주는 <title>과 <meta> 태그를 추가합니다. React 19.2(2025년 10월)는 <Activity>와 useEffectEvent를 추가했습니다.

아래의 모든 예제는 React 19.2에서 실행됩니다. 다음은 액션입니다. 폼에 넘긴 async 함수이며, 대기 상태와 반환된 오류는 useActionState가 처리합니다.

필드를 비운 채로 Save를 누르면 오류가 보이고, 이름을 입력하고 저장하면 버튼이 800ms 동안 "Saving..."으로 표시됩니다. 오류를 위한 useState도, isLoading 플래그도, e.preventDefault()도 없습니다.

액션과 폼 액션

React 18에서는 폼마다 같은 단계를 작성했습니다. 기본 제출을 막고, 로딩 플래그를 설정하고, 요청을 기다리고, 오류를 저장하고, 플래그를 해제합니다. React 19는 트랜지션 안에서 비동기 작업을 하는 함수를 액션이라고 부르며, 그 관리를 대신해 줍니다.

  • 이제 startTransition(과 useTransition)이 async 함수를 받습니다. isPending은 함수 전체가 끝날 때까지 true로 유지됩니다.
  • <form action={fn}>은 폼이 제출되면 폼의 FormData와 함께 fn을 호출합니다. 액션으로 실행되며, 끝나면 React가 폼의 비제어 필드를 초기화합니다.
  • <button formAction={fn}>은 제출 버튼 하나에 자기만의 액션을 줍니다.
  • useActionState는 액션이 마지막으로 반환한 값을 보관하므로, 액션이 오류 메시지나 저장된 레코드를 반환할 수 있습니다.
  • react-dom의 useFormStatus는 폼 안의 컴포넌트가 props를 내려받지 않고도 그 폼이 제출 중인지 읽게 해 줍니다. 부모 <form>이 필요하므로 여기서는 정적 블록으로 보여 줍니다.
import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending } = useFormStatus();
    return <button disabled={pending}>{pending ? 'Sending...' : 'Send'}</button>;
}

useOptimistic

useOptimistic은 액션이 아직 실행 중인 동안 예상되는 결과를 보여 주고, 액션이 끝나면 실제 state로 돌아갑니다. 좋아요 카운터는 클릭하는 순간 올라가고, 요청이 실패하면 다시 내려갈 수 있습니다.

const [optimisticLikes, addOptimisticLike] = useOptimistic(likes, (current, delta) => current + delta);

useOptimistic 페이지에는 일부러 실패하는 가짜 API를 쓴 실행 가능한 예제가 있어서 되돌리기를 지켜볼 수 있습니다.

use API

use는 프로미스나 컨텍스트에서 값을 읽습니다. 프로미스라면 완료될 때까지 컴포넌트를 일시 중단하고, 그동안 가장 가까운 <Suspense>가 대체 UI를 보여 줍니다. 다른 모든 훅과 달리 use는 if나 반복문 안에서, 그리고 조기 return 뒤에서도 호출할 수 있습니다.

React 19에서는 <ThemeContext.Provider> 대신 <ThemeContext value="dark">를 직접 렌더링할 수도 있습니다. 이 예제에는 둘 다 나옵니다.

prop으로서의 ref와 ref 클린업

이제 함수 컴포넌트는 다른 prop처럼 ref를 받습니다. DOM 요소까지 ref를 전달하려고 forwardRef로 감쌀 필요가 더 이상 없습니다.

ref 콜백은 클린업 함수를 반환할 수도 있습니다. React는 요소가 화면에서 사라질 때 이펙트의 클린업과 같은 방식으로 그것을 호출합니다. 아무것도 반환하지 않는 콜백은 React 18처럼 제거될 때 여전히 null과 함께 호출됩니다.

버튼을 몇 번 클릭하고 콘솔을 읽어 보세요. 제거될 때마다 클린업 줄이, 돌아올 때마다 attached to DIV가 기록됩니다.

문서 메타데이터

컴포넌트 어디에서든 <title>, <meta>, <link>를 렌더링할 수 있고, React가 그것들을 <head>로 옮깁니다. 페이지 컴포넌트가 react-helmet 같은 라이브러리 없이 자기 제목을 설정할 수 있습니다.

미리보기에는 보여 줄 브라우저 탭이 없으므로 이펙트가 대신 document.title을 기록합니다. 글을 바꾸면 기록되는 제목이 따라 바뀝니다.

스타일시트도 같은 대우를 받았습니다. 컴포넌트 안의 <link rel="stylesheet" href="..." precedence="default" />는 우선순위 순서로 head에 한 번만 삽입되며, React는 그것이 필요한 내용을 보여 주기 전에 스타일시트를 기다립니다. 여러 컴포넌트가 렌더링하는 <script async src="...">는 한 번만 불러옵니다. react-dom은 리소스 힌트를 위한 preload, preinit, preconnect, prefetchDNS도 export합니다.

Activity와 useEffectEvent(React 19.2)

<Activity mode="hidden">은 자식의 state를 버리지 않고 숨깁니다. React는 DOM을 숨기고, 이펙트의 클린업을 실행하고, mode가 다시 "visible"이 되면 모든 것을 복원합니다. 사용자가 돌아올 것으로 예상되는 탭과 패널에 적합합니다.

메모를 입력하고 About으로 갔다가 돌아와 보세요. 텍스트가 그대로 있고, 콘솔에는 숨겨진 동안 이펙트가 정리된 것이 표시됩니다. <Activity> 래퍼를 {tab === 'notes' && <Notes />}로 바꾸면 전환할 때마다 메모가 사라집니다.

useEffectEvent는 함수를 이펙트의 의존성에서 빼냅니다. 함수는 항상 최신 props와 state를 보지만, 그것들이 바뀌어도 이펙트가 다시 시작되지 않습니다.

function ChatRoom({ roomId, theme }) {
    const onConnected = useEffectEvent(() => {
        showNotification('Connected!', theme);
    });

    useEffect(() => {
        const connection = createConnection(roomId);
        connection.on('connected', () => onConnected());
        connection.connect();
        return () => connection.disconnect();
    }, [roomId]); // theme is not a dependency
}

19.2는 서버 컴포넌트를 위한 cacheSignal(cache() 결과가 더 이상 필요 없을 때를 알려 줍니다)과 Chrome DevTools 성능 패널의 Performance Tracks도 추가했습니다. React 19.1은 현재 컴포넌트를 어떤 컴포넌트들이 렌더링했는지 반환하는 개발 전용 함수 captureOwnerStack을 추가했습니다.

서버 컴포넌트와 서버 함수

서버 컴포넌트는 서버에서(또는 빌드 시점에) 미리 실행되어 그 출력을 브라우저로 보내는 컴포넌트입니다. async일 수 있고 데이터베이스를 직접 읽을 수 있으며, 그 코드는 클라이언트로 절대 전달되지 않습니다. 서버 함수(처음에는 Server Actions라고 불렸습니다)는 'use server'로 표시한 async 함수로, 클라이언트 컴포넌트가 일반 함수처럼 호출할 수 있습니다. 둘 다 Next.js App Router처럼 이를 지원하는 번들러를 가진 프레임워크가 필요하므로 이 에디터에서는 실행할 수 없습니다.

// A Server Component: async, runs on the server only
export default async function Notes() {
    const notes = await db.notes.findMany();
    return notes.map((note) => <Note key={note.id} note={note} />);
}

// A Server Function, usable as a form action
async function createNote(formData) {
    'use server';
    await db.notes.create({ text: formData.get('text') });
}

제거되고 바뀐 API

React 19는 몇 년 동안 지원 중단 상태였던 API를 제거했습니다.

제거됨대신 사용할 것
ReactDOM.render, ReactDOM.hydratereact-dom/client의 createRoot, hydrateRoot
unmountComponentAtNoderoot.unmount()
findDOMNodeDOM 요소에 붙인 ref
문자열 ref(ref="input")useRef 또는 ref 콜백
레거시 컨텍스트(contextTypes, getChildContext)static contextType 또는 use와 함께 쓰는 createContext
propTypes 검사(이제 조용히 무시됨)TypeScript, 또는 직접 만든 검사
함수 컴포넌트의 defaultProps매개변수 기본값
react-dom/test-utilsreact의 act, 나머지는 React Testing Library
react-test-renderer/shallowreact-shallow-renderer 패키지
React.createFactoryJSX

클래스 컴포넌트는 defaultProps를 유지합니다. 오류 보고 방식도 바뀌었습니다. 에러 바운더리가 잡은 오류는 console.error로 한 번 기록되고, 잡히지 않은 오류는 window.reportError로 가며, createRoot는 직접 보고할 수 있도록 onCaughtError와 onUncaughtError 옵션을 받습니다.

React 19로 업그레이드하기

먼저 React 18.3으로 업그레이드하세요. 18.2와 똑같지만 지원 중단된 API와 React 19에 필요한 다른 변경 사항에 대해 경고하므로, 앱이 아직 동작하는 동안 고칠 수 있습니다. React 19는 최신 JSX 변환도 요구하는데, Vite와 현재의 모든 프레임워크는 이미 그것을 씁니다. 그다음 React 19를 설치하세요.

npm install react@^19 react-dom@^19
npm install --save-dev @types/react@^19 @types/react-dom@^19

공식 업그레이드 가이드는 기계적인 변경을 위한 codemod를 안내합니다. 첫 번째는 문자열 ref, act import, ReactDOM.render를 다루고, 두 번째는 새 타입에 맞게 TypeScript 코드를 업데이트합니다.

npx codemod@latest react/19/migration-recipe
npx types-react-codemod@latest preset-19 ./src

업그레이드한 뒤 액션이나 새 훅을 도입하라고 강제하는 것은 없습니다. 기존 컴포넌트, 훅, 클래스 컴포넌트는 계속 동작하며, 폼을 하나씩 <form action>으로 옮겨도 됩니다.

자주 묻는 질문

React 19는 언제 출시되었나요?

React 19는 2024년 12월에 안정 버전이 되었습니다. React 19.1은 2025년 3월에, React 19.2는 2025년 10월에 뒤를 이었습니다.

React 19의 가장 큰 변화는 무엇인가요?

액션(대기 상태, 오류, 폼을 대신 처리하는 async 함수), useActionState, useOptimistic, use 훅, 일반 prop으로서의 ref, Provider로서의 <Context>, 그리고 문서 head로 옮겨지는 <title>과 <meta> 태그입니다. 서버 컴포넌트는 프레임워크용으로 안정화되었습니다.

React 19에서 forwardRef는 지원 중단되었나요?

이제 함수 컴포넌트가 ref를 일반 prop으로 받을 수 있으므로 새 코드에는 forwardRef가 필요 없습니다. 여전히 동작하며, React 팀은 향후 버전에서 지원 중단될 것이라고 밝혔습니다.

React 19가 React 18 앱을 깨뜨리나요?

주로 제거된 API를 통해서입니다: ReactDOM.render, findDOMNode, 문자열 ref, 레거시 컨텍스트, propTypes, 함수 컴포넌트의 defaultProps. React 19는 최신 JSX 변환도 요구합니다. 먼저 React 18.3으로 업그레이드해서 지원 중단 경고를 고친 다음, React 19 codemod를 실행하세요.

React 19를 쓰려면 Next.js가 필요한가요?

아니요. 액션, 새 훅, prop으로서의 ref, 메타데이터는 모두 Vite로 만든 일반 클라이언트 앱에서 동작합니다. 서버 컴포넌트와 서버 함수에는 Next.js처럼 이를 지원하는 프레임워크가 필요합니다.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기