Ray Book
React 18에서 19까지

React 18에서 19까지, 무엇이 바뀌었나

React 18의 Concurrent Rendering부터 React 19.2의 View Transitions까지, 약 3년간의 변화를 한눈에 정리합니다.

reactreact18react19overview

왜 이 시리즈인가

React 18이 2022년 3월에 출시되고, React 19가 2024년 12월에 나왔습니다. 그리고 19.1, 19.2를 거치며 2026년 현재 19.2.1이 최신 안정 버전입니다. 약 3년간의 변화는 React의 역사에서 가장 큰 패러다임 전환 중 하나로 기록될 만합니다.

이 시리즈는 그 변화를 정리합니다. 단순히 "어떤 API가 추가되었다"를 나열하는 것이 아니라, 각 변화가 왜 필요했고, 어떻게 사용해야 베스트 프랙티스인지 를 다룹니다. 7편에 걸쳐 살펴볼 내용은 다음과 같습니다.

  1. 이 글 , 전체 변화의 큰 그림
  2. Concurrent Rendering의 본질, useTransition, useDeferredValue, Suspense
  3. 외부 상태와 동기화, useSyncExternalStore와 tearing
  4. Actions와 폼 처리, useActionState, useFormStatus, useOptimistic
  5. Server Components와 use(), RSC와 새로운 데이터 패칭
  6. ref와 Context의 변화, forwardRef의 종말
  7. 19.1과 19.2의 새 기능, Activity, View Transitions, 메타데이터

React 18, Concurrent의 시대를 열다

React 18 (2022.03) 의 핵심은 Concurrent Rendering 입니다. 이전까지 React의 렌더링은 시작되면 끝까지 동기적으로 진행되는 것이었습니다. 한번 렌더링이 시작되면 메인 스레드를 점유하여, 큰 트리를 렌더링하는 동안 사용자 입력에 반응할 수 없었습니다.

Concurrent Rendering은 이 모델을 바꿨습니다. React는 렌더링을 중간에 멈추고, 다시 시작하고, 우선순위를 조정 할 수 있게 되었습니다. 사용자 입력 같은 긴급한 작업은 끼어들 수 있고, 무거운 렌더링은 백그라운드에서 진행됩니다.

이 변화 위에 새로운 Hook과 API들이 등장했습니다.

React 18의 새 기능들

useTransition , 상태 업데이트를 "긴급하지 않은 것"으로 표시합니다. React는 이 업데이트를 백그라운드에서 처리하고, 그 사이에 새로운 긴급한 업데이트가 들어오면 우선 처리합니다.

useDeferredValue , 값의 업데이트를 지연시킵니다. 비싼 렌더링이 입력 응답성을 망치는 것을 막을 때 사용합니다.

useId , SSR과 클라이언트 간에 안전하게 일치하는 고유 ID를 생성합니다. 접근성 속성 (aria-labelledby 등) 에 자주 사용됩니다.

useSyncExternalStore , 외부 스토어 (Zustand, Redux 등) 를 React의 렌더링과 안전하게 동기화합니다. Concurrent Rendering이 도입한 tearing 문제를 해결하기 위해 만들어졌습니다.

useInsertionEffect , CSS-in-JS 라이브러리를 위한 특수 Hook입니다. DOM 변경 전에 스타일을 주입할 수 있게 해줍니다.

Automatic Batching , React 18 이전에는 Promise, setTimeout, 네이티브 이벤트 핸들러 안의 state 업데이트가 배치되지 않았습니다. 18부터는 모든 곳에서 자동으로 배치됩니다.

Suspense for SSR , 서버 사이드 렌더링에서도 Suspense를 사용할 수 있습니다. 페이지의 일부가 준비되지 않아도 나머지를 먼저 보낼 수 있습니다 (Streaming SSR).

React 19, 패러다임의 전환

React 19 (2024.12) 는 React 18이 깔아둔 토대 위에서 데이터 처리 방식 자체 를 바꿨습니다. 핵심은 두 가지입니다, Server Components의 안정화와 Actions의 도입 .

Server Components

Server Components는 서버에서만 실행되는 컴포넌트입니다. 데이터베이스에 직접 접근하고, 환경 변수를 읽고, 서버 전용 라이브러리를 사용할 수 있습니다. 그 결과 HTML과 함께 직렬화되어 클라이언트로 전송되며, 클라이언트 번들에는 포함되지 않습니다.

이전에는 데이터를 가져오려면 클라이언트에서 useEffect를 쓰거나, 별도의 데이터 패칭 라이브러리 (TanStack Query 등) 를 사용해야 했습니다. Server Components는 이 패턴을 컴포넌트 자체가 데이터를 직접 가져오는 방식으로 바꿉니다.

// Server Component
async function UserProfile({ userId }) {
  const user = await db.users.findById(userId); // 직접 DB 접근
  return <div>{user.name}</div>;
}

Actions

Actions는 폼 제출과 같은 mutation을 다루는 새로운 패턴입니다. 이전에는 폼 제출 시 로딩 상태, 에러 상태, 낙관적 업데이트를 모두 수동으로 관리해야 했습니다. Actions는 이를 React 자체의 기능으로 통합했습니다.

function UpdateForm() {
  const [state, formAction, isPending] = useActionState(
    async (prevState, formData) => {
      const result = await updateUser(formData);
      return { error: result.error ?? null };
    },
    { error: null }
  );

  return (
    <form action={formAction}>
      <input name="name" />
      <button disabled={isPending}>저장</button>
      {state.error && <p>{state.error}</p>}
    </form>
  );
}

useActionState, useFormStatus, useOptimistic, 세 가지 Hook이 Actions 패러다임의 핵심입니다.

use() Hook

use()는 React 19의 가장 흥미로운 추가 중 하나입니다. Promise나 Context를 조건문 안에서도 읽을 수 있게 해주는 특수한 함수입니다.

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise); // Promise를 직접 읽음
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

기존 Hook들은 컴포넌트 최상단에서만 호출할 수 있다는 규칙이 있었습니다. use()는 그 규칙에서 자유롭습니다. 조건문이나 반복문 안에서도 사용 가능합니다.

그 외 19의 변화

  • ref를 일반 prop으로 , forwardRef 없이 함수 컴포넌트가 ref를 받을 수 있습니다
  • <Context> as Provider , <MyContext.Provider> 대신 <MyContext> 만으로 충분합니다
  • Document Metadata , 컴포넌트 안에서 <title>, <meta> 를 직접 사용하면 React가 자동으로 <head> 로 옮겨줍니다
  • Asset Preloading , preload, preinit 같은 API로 리소스를 미리 로드할 수 있습니다

React 19.1, 19.2, 안정화와 새 기능

19.0이 큰 패러다임 전환이었다면, 19.1과 19.2는 그 위에 작지만 의미 있는 기능을 추가했습니다.

React 19.1 (2025.03)

  • Owner Stacks , captureOwnerStack API를 통해 어떤 컴포넌트가 다른 컴포넌트의 렌더링을 "소유"했는지를 추적하는 새로운 디버깅 기능
  • 에러 보고 메커니즘 개선
  • Server Components 디버깅 향상

React 19.2 (2025.10)

  • Activity API , 컴포넌트를 "비활성 상태"로 두고 상태를 유지할 수 있는 API입니다. 탭 전환 같은 상황에서 컴포넌트를 unmount하지 않고 숨길 수 있습니다
  • View Transitions (실험적) , 브라우저의 View Transitions API를 React 렌더링과 통합하기 위한 작업이 진행 중이며, 19.2 시점에서는 Canary/Experimental 채널에서 사용 가능합니다
  • Performance Tracks , Chrome DevTools의 Performance 패널에 React의 렌더링 작업을 표시합니다

시리즈 진행 방향

다음 글부터는 각 주제를 깊이 들여다봅니다. 단순히 API 사용법이 아니라, 왜 이 API가 필요했는지언제 어떻게 써야 베스트 프랙티스인지 를 함께 다룹니다.

다음 글에서는 Concurrent Rendering의 본질을 살펴봅니다. useTransitionuseDeferredValue가 정확히 무엇을 다르게 하는지, Suspense가 왜 데이터 패칭과 결합되는지, 그리고 이 모든 것이 어떻게 한 줄기로 연결되는지를 정리합니다.