Home
PostsAbout
react

번역) React 19 Beta

React 19 베타의 내용

2024-08-20

본문: https://react.dev/blog/2024/04/25/react-19

React 19 Beta를 이제 npm에서 사용하실 수 있습니다! 이번 포스트에서는 React 19의 새로운 기능들에 대해 전반적으로 살펴보고 그들을 어떻게 도입할 수 있는지 소개합니다.

2024년 4월 25일 by The React Team


React 19 Beta를 이제 npm에서 사용하실 수 있습니다!

우리는 React 19 Beta Upgrade Guide에서 당신의 App에 React 19를 도입하기 위한 단계적인 가이드를 공유하였습니다. 이번 포스트에서는 React 19의 새로운 기능들을 전반적으로 살펴보고 그들을 어떻게 도입할 수 있는 공유합니다.

중요한 변경 리스트를 확인하려면 Upgrade Guide을 참조하세요.


What’s new in React 19

Actions

React apps에서 Actions을 사용하는 가장 흔한 케이스는 data mutation을 실행하고 응답에 따라 상태를 업데이트하는 경우입니다. 예를 들어 유저가 그들의 이름을 바꾸기 위해 폼을 제출했을 때, 당신은 API 요청을 만들 것이고, 응답을 처리할 것입니다. 과거에는 정석적으로 pending states, errors, optimistic updates 그리고 sequential requests를 처리해야 했습니다.

예를 들어, 당신은 다음과 같이 useState 에서 pending과 error 상태를 처리할 수 있었습니다.

 // Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

React 19는 transitions에서 비동기 함수를 사용하여 pending states, errors, forms, 그리고 optimistic updates를 자동으로 처리하는 기능을 지원합니다.

예를 들어, 다음과 같이 useTransition 을 활용하여 pending state를 처리할 수 있습니다.

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

비동기 transition은 즉시 isPending 상태를 true로 바꾸고, 비동기 요청을 생성합니다. 그리고 어떤 transitions 이후에라도 즉시 isPending 상태를 false로 바꿉니다. 이 덕분에 데이터가 변경되는 동안 현재 UI가 데이터에 반응하여 상호작용할 수 있게 됩니다.

컨벤션에 따라 비동기 transitions을 사용하는 함수를 “Actions”이라고 합니다. Actions은 자동으로 데이터 제출을 관리합니다:

  • Pending state: Actions은 요청이 시작될 때 시작되어 최종 상태 업데이트가 커밋되면 자동으로 초기화되는 pending state를 제공합니다.
  • Optimistic updates: Actions은 요청이 제출되는 동안 사용자에게 즉각적인 피드백을 표시할 수 있도록 새로운 useOptimistic 훅을 지원합니다.
  • Error handling: Actions은 오류 처리 기능을 제공하므로 요청이 실패할 때 Error Boundaries를 표시하고 optimistic updates를 자동으로 원래 값으로 되돌릴 수 있습니다.
  • Forms: <form> 요소는 이제 Actions 및 formAction props에 함수 전달을 지원합니다. action prop에 함수를 전달하면 기본적으로 Actions이 사용되며 제출 후 양식이 자동으로 초기화됩니다.

Actions을 기반으로, React 19는 useOptimistic이라는 훅을 통해 optimistic updates를 관리할 수 있도록 하고 React.useActionState를 통해 Actions의 일반적인 케이스들을 처리할 수 있도록 합니다. react-dom 에는 <form> Actions을 추가하여 폼을 자동으로 처리하고 useFormStatus 을 통해 폼에서의 대부분의 케이스를 지원합니다.

React 19에서 위의 예시는 다음과 같이 단순해집니다.

// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
      return null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

다음 섹션에서는, React 19의 새로운 기능들을 각각 자세히 살펴보겠습니다.

New hook: useActionState

Actions의 대부분의 케이스를 쉽게 처리하기 위해, useActionState 라는 훅을 만들었습니다.

const [error, submitAction, isPending] = useActionState(
  async (previousState, newName) => {
    const error = await updateName(newName);
    if (error) {
      // You can return any result of the action.
      // Here, we return only the error.
      return error;
    }

    // handle success
    return null;
  },
  null,
);

useActionState 는 함수(the “Action”)를 받아서 호출하기 위한 wrapped Action을 반환합니다. 이는 각 액션이 조합되기 때문에 동작합니다. wrapped Action이 호출되면, useActionState 는 Action의 가장 마지막 결과를 data 로 반환하고, Action의 pending state를 pending 으로 반환합니다.

💡 React.useActionState 는 이전 Canary 릴리즈에서 ReactDOM.useFormState 으로 불렸습니다. 하지만 이름을 수정했고 useFormState 을 deprecate 했습니다.

#28491을 참조하세요.

더 많은 정보를 원한다면 useActionState을 살펴보세요.

React DOM: <form> Actions

Actions는 또한 react-dom 의 새로운 <form> 기능들과 통합되었습니다. 액션으로 폼을 자동으로 제출할 수 있도록 <form>, <input> 그리고 <button> 요소에 actionformAciton props로 함수를 전달할 수 있습니다.

<form action={actionFunction}>

<form> Action이 성공하면 React는 자동으로 폼을 비제어 컴포넌트로 초기화합니다. 만약 폼을 직접 초기화할 필요가 있다면 requestformReset 이라는 새로운 React DOM API를 호출할 수 있습니다.

더 많은 정보를 원한다면, react-dom 공식문서의 <form>, <input> 그리고 <button>을 살펴보세요.

React DOM: New hook: useFormStatus

디자인 시스템에서 props drilling 없이 그들이 속한 폼의 정보에 대한 접근이 필요한 디자인 컴포넌트를 작성하는 일은 흔합니다. Context를 통해서도 이렇게 할 수 있지만, 이렇게 흔한 일을 좀 더 쉽게 하기 위해서 우리는 useFormStatus 라는 새로운 훅을 추가하였습니다.

import {useFormStatus} from 'react-dom';

function DesignButton() {
  const {pending} = useFormStatus();
  return <button type="submit" disabled={pending} />
}

useFormStatus 는 마치 폼이 Context provider인것처럼 부모 <form>요소의 상태를 읽을 수 있습니다.

더 많은 정보를 원한다면, react-dom 공식문서의 useFormStatus을 살펴보세요.

New hook: useOptimistic

또다른 흔한 UI 패턴 중 하나는 data mutation을 수행 시 비동기 요청 중에 낙관적(optimistically)으로 마지막 결과 상태를 보여주는 것입니다. React 19에서는 useOptimistic 훅을 추가하여 이 작업을 더 쉽게 합니다.

function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async formData => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

useOptimistic 훅은 updateName 요청이 진행중일 때 즉시 optimisticName 을 렌더링합니다. 업데이트가 끝나거나 혹은 에러가 발생했을 때 React는 자동으로 currentName 값으로 UI를 돌려놓습니다.

더 많은 정보를 원한다면, useOptimistic을 살펴보세요.

New API: use

React 19에는 렌더링에 사용되는 리소스를 읽는 새로운 API use 가 추가됩니다.

예를 들어 use 를 통해 프로미스를 읽을 수 있고 React는 프로미스를 resolve하기 전까지 일시 중단됩니다.

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` will suspend until the promise resolves.
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // When `use` suspends in Comments,
  // this Suspense boundary will be shown.
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

💡 use 는 렌더링 과정에서 생성된 프로미스를 지원하지 않습니다.

만약 당신이 렌더링 과정에서 생성된 프로미스를 use 에 넘기려고 한다면 React는 다음과 같이 경고할 것입니다.
A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.
이 문제를 해결하려면 Suspense 기반 라이브러리 또는 프로미스 캐싱을 지원하는 프레임워크에서 프로미스를 전달해야 합니다. 향후에는 렌더링에서 프로미스를 더 쉽게 캐싱할 수 있는 기능을 제공할 계획입니다.

또한 use 를 통해 context를 읽을 수 있습니다. early return과 같이 조건부로 context를 읽는 것도 가능합니다.

import {use} from 'react';
import ThemeContext from './ThemeContext'

function Heading({children}) {
  if (children == null) {
    return null;
  }
  
  // This would not work with useContext
  // because of the early return.
  const theme = use(ThemeContext);
  return (
    <h1 style={{color: theme.color}}>
      {children}
    </h1>
  );
}

use API는 훅과 비슷하게 렌더링 안에서만 호출될 수 있습니다. 훅과 다른 점은, 조건부로 호출될 수 있다는 점입니다. 우리는 앞으로 use 를 이용하여 렌더링 안에서 더욱 다양한 리소스를 사용할 수 있도록 지원할 예정입니다.

더 많은 정보를 원한다면, use를 살펴보세요.

React Server Components

Server Components

Server Components는 번들링 전에 클라이언트 애플리케이션 또는 SSR 서버와 분리된 환경에서 컴포넌트를 미리 렌더링할 수 있는 새로운 옵션입니다. 이 별도의 환경이 React Server 컴포넌트에서 “Server”입니다. 서버 컴포넌트는 CI 서버에서 빌드 시 한 번 실행하거나 웹 서버를 사용하여 각 요청에 대해 실행할 수 있습니다.

React 19에는 Canary 채널에 포함된 모든 React Server Components 기능이 포함되어 있습니다. 즉, Server Components와 함께 제공되는 라이브러리는 이제 Full-stack React Architecture를 지원하는 프레임워크에서 사용하기 위해 react-server export condition으로 React 19를 피어 의존성으로 타겟팅할 수 있습니다.

💡 Server Components를 사용하기 위해서 어떻게 해야 하나요?

React 19의 React 서버 컴포넌트는 stable 메이저 버전 간에 중단되지 않지만, React 서버 컴포넌트 번들러 또는 프레임워크를 구현하는 데 사용되는 기본 API는 semver를 따르지 않으며 React 19.x의 마이너 버전 간에 중단될 수 있습니다.
번들러 또는 프레임워크로서 React Server Components를 지원하려면 특정 React 버전에 고정하거나 Canary 릴리스를 사용하는 것이 좋습니다. 향후에도 번들러 및 프레임워크와 협력하여 React Server Components를 구현하는 데 사용되는 API를 안정화할 예정입니다.

Server Actions

Server Actions은 Client Components가 서버에서 실행되는 비동기 함수를 호출할 수 있도록 합니다.

"use server" 지시어와 함께 Server Action이 정의되면, 당신의 프레임워크는 자동으로 서버 함수에 대한 참조를 생성하고 Client Component에 그 참조를 전달합니다. 해당 함수가 클라이언트에서 호출되면, React는 함수를 실행하기 위한 요청을 서버에 보내고, 결과를 반환합니다.

💡 Server Components를 위한 지시어는 없습니다.

흔한 오해 중 하나가 Server Components가 "use server" 에 의해 지시된다는 것입니다. 하지만 Server Components를 위한 지시어는 없습니다. "use server" 지시어는 Server Actions을 위한 것입니다.

더 많은 정보를 원한다면, Directives을 참조하세요.

Server Actions는 Server Components에서 생성될 수 있으며 Client Components에 props으로 전달되거나 Client Components에서 import되어 사용될 수 있습니다.

더 많은 정보를 원한다면, React Server Actions을 살펴보세요.

Improvements in React 19

ref as a prop

React 19부터 함수형 컴포넌트에 prop으로서 ref에 접근할 수 있습니다.

function MyInput({placeholder, ref}) {
  return <input placeholder={placeholder} ref={ref} />
}

//...
<MyInput ref={ref} />

새로운 함수형 컴포넌트는 더 이상 forwardRef가 필요하지 않습니다. 그리고 우리는 codemod를 발행하여 당신의 컴포넌트가 새로운 ref prop을 사용하도록 할 예정입니다. 앞으로의 버전에는 forwardRef를 deprecate하고 제거할 예정입니다.

💡 classes로 전달되는 refs 는 props로 전달되지 않습니다. 왜냐하면 그들은 컴포넌트 인스턴스를 참조하기 때문입니다.

Diffs for hydration errors

react-dom에서 hydration errors를 보고하는 에러 리포팅을 개선하였습니다. 예를 들어 개발 환경에서 mismatch에 대한 아무런 정보 없이 여러 줄의 에러를 로깅하는 것 대신에:

Warning: Text content did not match. Server: “ServerClient: “Client”
  at span
  at App
Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.
Warning: Text content did not match. Server: “ServerClient: “Client”
  at span
  at App
Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.
Uncaught Error: Text content does not match server-rendered HTML.
  at checkForUnmatchedText
  …

이제는 mismatch에 대한 diff를 가지고 있는 단건 메시지를 로깅합니다.

Uncaught Error: Hydration failed because the server rendered HTML didn’t match the client. As a result this tree will be regenerated on the client. This can happen if an SSR-ed Client Component used:

- A server/client branch if (typeof window !== 'undefined').
- Variable input such as Date.now() or Math.random() which changes each time it’s called.
- Date formatting in a user’s locale which doesn’t match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.

It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.

https://react.dev/link/hydration-mismatch 

  <App>
    <span>
+    Client
-    Server

  at throwOnHydrationMismatch
  …

<Context> as a provider

React 19에서 <Context.Provider> 대신에 <Context>를 provider처럼 렌더링할 수 있습니다.

const ThemeContext = createContext('');

function App({children}) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );  
}

새로운 Context providers는 <Context>를 사용할 수 있으며 우리는 codemod를 발행하여 이미 존재하는 providers를 전환할 예정입니다. 앞으로의 버전에 우리는 <Context.Provider>를 deprecate할 예정입니다.

Cleanup functions for refs

이제 ref 콜백이 반환하는 클린업 합수를 지원합니다.

<input
  ref={(ref) => {
    // ref created

    // NEW: return a cleanup function to reset
    // the ref when element is removed from DOM.
    return () => {
      // ref cleanup
    };
  }}
/>

컴포넌트가 언마운트될 때, React는 ref 의 콜백 함수로부터 반환되는 클린업 함수를 호출할 것입니다. 이것은 DOM refs, refs to class components, useImperativeHandle에서 동작할 것입니다.

💡 이전에 React는 컴포넌트를 언마운트할 때 ref 함수를 null 과 함께 호출하였습니다. 만약 당신의 ref 가 클린업 함수를 리턴한다면, React는 이제 이 과정을 스킵할 것입니다.

앞으로 우리는 컴포넌트를 언마운트할 때 refnull 과 함께 호출하는 것을 deprecate할 예정입니다.

ref 클린업 함수의 등장으로, ref 콜백으로부터 반환되는 그 어떤 것일지라도 TypeScript에 의해 reject될 것입니다. 이를 고치는 방법은 implicit returns 사용을 중지하는 것입니다.

- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />

기존의 코드는 HTMLDivElement 의 인스턴스를 리턴하였고 TypeScript는 이것이 클린업 함수인지, 아니면 클린업 함수를 리턴하고 싶은게 아니었는지 알지 못합니다.

당신은 no-implicit-ref-callback-return 을 통해 이러한 패턴을 codemod할 수 있습니다.

useDeferredValue initial value

React 19에서는 useDeferredValueinitialValue가 추가됩니다.

function Search({deferredValue}) {
  // On initial render the value is ''.
  // Then a re-render is scheduled with the deferredValue.
  const value = useDeferredValue(deferredValue, '');
  
  return (
    <Results query={value} />
  );
}

initialValue가 주어지면, useDeferredValueinitialValue를 컴포넌트의 초기 렌더링 value로 반환합니다. 그리고 백그라운드에서 deferredValue 리턴값으로 리렌더링을 스케줄링합니다.

더 많은 정보를 원한다면, useDeferredValue를 참조하세요.

Support for Document Metadata

HTML에서 <title> , <link> , <meta> 등과 같은 document 메타데이터 태그들은 document의 <head> 안에 위치하도록 되어있습니다. React에서 어떤 메타데이터가 App에 적절한지 결정하는 컴포넌트는 아마 당신이 <head>를 렌더링하는 곳으로부터 매우 멀리 떨어져 있거나 어쩌면 React가 <head>를 렌더링조차 안하고 있을 수도 있습니다. 과거에는 이러한 요소들이 수동으로 effect안에 삽입되어야 하거나, react-helmet 등의 라이브러리를 이용해야 했거나, React 어플리케이션을 서버가 렌더링하는 시점에 조심스럽게 핸들링했어야 했습니다.

React 19에서는 document 메타데이터 태그들을 컴포넌트에 렌더링할 수 있도록(natively) 지원합니다.

function BlogPost({post}) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Josh" />
      <link rel="author" href="https://twitter.com/joshcstory/" />
      <meta name="keywords" content={post.keywords} />
      <p>
        Eee equals em-see-squared...
      </p>
    </article>
  );
}

React가 이 컴포넌트를 렌더링 할 때, <title> <link> 그리고 <meta> 태그를 볼 수 있고, 그들을 document의 <head> 구역으로 자동으로 호이스팅합니다. 이러한 메타데이터 태그들을 natively하게 지원함으로써, 그 메타데이터들이 client-only apps, streaming SSR, 그리고 Server Components에서도 정상적으로 동작할 것이라고 확신합니다.

💡 당신은 여전히 메타데이터 라이브러리를 사용하고 싶을 수 있습니다.

간단한 케이스에 대해서는 Document 메타데이터를 태그로 렌더링하는 것이 알맞을지 모릅니다. 하지만 라이브러리는 현재 라우트에 기반한 specific한 메타데이터로 generic한 메타데이터를 오버라이드 하는 등의 강력한 기능을 제공할 수 있습니다. 이러한 기능들은 react-helmet 과 같은 라이브러리나 프레임워크를 사용하는 것이 그들을 대체하는 것보다 쉬울 수 있습니다.

더 많은 정보를 원한다면, <title>, <link>, 그리고 <meta>에 대한 공식문서를 참조하세요.

Support for stylesheets

externally linked (<link rel="stylesheet" href="...">)그리고 inline (<style>...</style>) 스타일시트는 둘 다 스타일 우선순위 규칙 때문에 DOM의 어느 위치에 배치하는지가 까다롭습니다.

컴포넌트 내에서 구성성(composability)을 허용하는 스타일시트 기능을 구축하는 것은 어렵기 때문에 사용자는 종종 모든 스타일을 의존할 수 있는 컴포넌트에서 멀리 떨어진 곳에서 로드하거나 이러한 복잡성을 캡슐화하는 스타일 라이브러리를 사용합니다.

React 19에서는 이러한 복잡성을 해결하고 스타일시트를 기본 지원하여 클라이언트의 동시성 렌더링(Concurrent Rendering)과 서버의 Streaming Rendering에 더욱 긴밀하게 통합할 수 있게 되었습니다. React에 스타일시트의 우선순위를 지정하면 DOM에서 스타일시트의 삽입 순서를 관리하고 해당 스타일 규칙에 의존하는 콘텐츠를 표시하기 전에 스타일시트(외부인 경우)가 로드되는지 확인합니다.

function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo" precedence="default" />
      <link rel="stylesheet" href="bar" precedence="high" />
      <article class="foo-class bar-class">
        {...}
      </article>
    </Suspense>
  )
}

function ComponentTwo() {
  return (
    <div>
      <p>{...}</p>
      <link rel="stylesheet" href="baz" precedence="default" />  <-- will be inserted between foo & bar
    </div>
  )
}

서버 사이드 렌더링 중에 React는 <head>에 스타일시트를 포함시켜 스타일시트가 로드될 때까지 브라우저가 페인팅하지 않도록 합니다. 이미 스트리밍을 시작한 후 스타일시트가 늦게 발견되는 경우, React는 스타일시트가 클라이언트의 <head>에 삽입되었는지 확인한 후 해당 스타일시트에 의존하는 Suspense boundary의 콘텐츠를 표시합니다.

클라이언트 사이드 렌더링 도중 React는 렌더링을 커밋하기 전에 새로 렌더링된 스타일시트가 로드될 때까지 기다립니다. 애플리케이션 내 여러 위치에서 이 컴포넌트를 렌더링하는 경우 React는 스타일시트를 document에 한 번만 포함합니다:

function App() {
  return <>
    <ComponentOne />
    ...
    <ComponentOne /> // won't lead to a duplicate stylesheet link in the DOM
  </>
}

스타일시트를 수동으로 로드하는 데 익숙한 사용자에게는 스타일시트에 의존하는 컴포넌트와 함께 해당 스타일시트를 찾을 수 있어 로컬 추론이 향상되고 실제로 의존하는 스타일시트만 로드하기가 더 쉬워질 수 있는 기회입니다.

스타일 라이브러리 및 번들러와의 스타일 통합도 이 새로운 기능을 채택할 수 있으므로 직접 스타일시트를 렌더링하지 않더라도 이 기능을 사용하도록 도구가 업그레이드되면 이점을 누릴 수 있습니다.

더 자세한 정보를 원한다면 <link> 그리고 <style>을 참조하세요.

Support for async scripts

HTML에서 일반 스크립트(

React 19에서는 스크립트 인스턴스 재배치 및 중복 제거를 관리할 필요 없이 컴포넌트 트리의 어느 곳에서나 스크립트에 실제로 의존하는 컴포넌트 내에서 렌더링할 수 있도록 비동기 스크립트에 대한 지원이 개선되었습니다.

function MyComponent() {
  return (
    <div>
      <script async={true} src="..." />
      Hello World
    </div>
  )
}

function App() {
  <html>
    <body>
      <MyComponent>
      ...
      <MyComponent> // won't lead to duplicate script in the DOM
    </body>
  </html>
}

모든 렌더링 환경에서 비동기 스크립트는 중복이 제거되어 여러 다른 컴포넌트에서 렌더링되더라도 React가 스크립트를 한 번만 로드하고 실행합니다.

서버 사이드 렌더링에서 비동기 스크립트는 <head>에 포함되며 스타일시트, 폰트, 이미지 프리로드와 같이 페인트를 차단하는 더 중요한 리소스보다 우선순위가 높습니다.

더 많은 정보를 원한다면, <script>을 참조하세요.

Support for preloading resources

초기 document가 로드되고 클라이언트 업데이트가 진행되는 동안 브라우저에게 어떤 자원이 최대한 빨리 로드되어야 하는지 알려주는 것은 페이지 성능에 드라마틱한 영향을 미칩니다.

React 19에는 브라우저 리소스를 로드하고 프리로드하는 새로운 API가 다수 포함되어 있어 비효율적인 리소스 로딩으로 인해 방해받지 않는 훌륭한 경험을 최대한 쉽게 구축할 수 있습니다.

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
  preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly
  preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
  preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
  prefetchDNS('https://...') // when you may not actually request anything from this host
  preconnect('https://...') // when you will request something but aren't sure what
}
<!-- the above would result in the following DOM/HTML -->
<html>
  <head>
    <!-- links/scripts are prioritized by their utility to early loading, not call order -->
    <link rel="prefetch-dns" href="https://...">
    <link rel="preconnect" href="https://...">
    <link rel="preload" as="font" href="https://.../path/to/font.woff">
    <link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
    <script async="" src="https://.../path/to/some/script.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

이러한 API를 사용하면 폰트와 같은 추가 리소스의 검색을 스타일시트 로딩에서 제외하여 초기 페이지 로드를 최적화할 수 있습니다. 또한 예상 탐색(anticipated navigation)에 사용되는 리소스 목록을 미리 가져온 다음 클릭 또는 마우스오버 시 해당 리소스를 미리 로드하여 클라이언트 업데이트 속도를 높일 수 있습니다.

더 많은 정보를 원한다면, Resource Preloading APIs을 참조하세요.

Compatibility with third-party scripts and extensions

서드 파티 스크립트 및 브라우저 확장 프로그램을 고려하도록 hydration을 개선했습니다.

hydrating 시 클라이언트에서 렌더링되는 요소가 서버의 HTML에서 찾은 요소와 일치하지 않으면 React는 클라이언트에서 강제로 다시 렌더링하여 콘텐츠를 수정합니다. 이전에는 서드 파티 스크립트나 브라우저 확장 프로그램에 의해 요소가 삽입된 경우 불일치 오류가 발생하고 클라이언트 렌더링이 트리거되었습니다.

React 19에서는 <head><body>안의 예기치 않은 태그를 건너뛰어 불일치 오류를 피할 수 있습니다. 관련 없는 hydration 불일치로 인해 전체 문서를 다시 렌더링해야 하는 경우, React는 서드 파티 스크립트 및 브라우저 확장 프로그램에 의해 삽입된 스타일시트를 그대로 유지합니다.

Better error reporting 

React 19에서는 중복을 제거하고 잡힌 오류와 잡히지 않은 오류를 처리하는 옵션을 제공하기 위해 오류 처리를 개선했습니다. 예를 들어, 렌더링에서 Error Boundary에 의해 잡힌 오류가 발생하면 이전에는 React가 오류를 두 번(원래 오류에 대해 한 번, 자동 복구에 실패한 후 한 번) 던진 다음 오류가 발생한 위치에 대한 정보와 함께 console.error를 호출했습니다.

이렇게 하면 잡힌 모든 오류에 대해 세 번의 오류가 발생했습니다:

Uncaught Error: hit
  at Throws
  at renderWithHooks
  …
Uncaught Error: hit    <-- Duplicate
  at Throws
  at renderWithHooks
  …

The above error occurred in the Throws component:
  at Throws
  at ErrorBoundary
  at App

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

React 19에서 우리는 모든 에러 정보가 포함된 단건의 에러를 로깅합니다.

Error: hit
  at Throws
  at renderWithHooks
  …

The above error occurred in the Throws component:
  at Throws
  at ErrorBoundary
  at App

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
  at ErrorBoundary
  at App

또한, onRecoverableError를 보완하기 위해 두 가지 새로운 루트 옵션을 추가했습니다:

  • onCaughtError: React가 Error Boundary에서 에러를 포착할 때 호출됩니다.
  • onUncaughtError: 에러가 발생했지만 Error Boundary에서 잡히지 않았을 때 호출됩니다.
  • onRecoverableError: 에러가 발생하고 자동으로 복구될 때 호출됩니다.

더 많은 정보와 예시를 원한다면, createRoot 그리고 hydrateRoot를 참조하세요.

Support for Custom Elements

React 19는 커스텀 요소에 대한 완전한 지원을 추가하고 Custom Elements Everywhere어에 대한 모든 테스트를 통과했습니다.

이전 버전에서는 React에서 사용자 정의 요소를 사용하는 것이 어려웠는데, 왜냐하면 React가 인식되지 않는 props을 properties가 아닌 attributes로 취급했기 때문입니다. React 19에서는 다음과 같은 전략으로 클라이언트에서 작동하는 properties와 SSR 중에 작동하는 properties에 대한 지원을 추가했습니다:

  • Server Side Rendering: 사용자 정의 요소에 전달된 props의 유형이 문자열, 숫자와 같은 기본값이거나 값이 true인 경우 attributes로 렌더링됩니다. 객체, 심볼, 함수 또는 값 false와 같이 원시 타입이 아닌 props은 생략됩니다.
  • Client Side Rendering: 사용자 정의 요소 인스턴스의 속성과 일치하는 props은 properties으로 할당되며, 그렇지 않은 경우 attributes으로 할당됩니다. React에서 사용자 정의 요소 지원의 설계와 구현을 주도한 Joey Arhar에게 감사드립니다.

업그레이드 방법 단계별 지침과 모든 주목할 만한 변경 사항이 궁금하다면 React 19 Upgrade Guide을 참조하세요.