본문 바로가기

React

React 19 출시 및 내용

React 19 버전이 출시되었다. 변경 점에 대해 알아보자.


1. Actions

1.1 개념

  • Actions는 React 앱에서 비동기로 데이터를 전송하고, 이에 따른 상태 업데이트를 단순화하기 위한 새 기능이다.
  • 요청 대기 상태(loading), 에러 처리, 낙관적 업데이트 등을 자동으로 관리해준다.

1.2 예시 코드

기존 방식(대기 상태와 에러를 직접 관리)

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={(e) => setName(e.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </div>
  );
}
 

React 19 - useTransition로 대기 상태 자동 관리

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={(e) => setName(e.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </div>
  );
}

2. useActionState 훅

  • useActionState는 액션(서버 요청) 로직을 한 곳에 모아, 결과 상태(에러, 응답 등)와 대기 상태를 간편하게 제어하는 훅이다.
  • 반환값으로 [actionResult, submitAction, isPending] 형식(또는 [에러, 액션함수, 대기상태])을 제공한다.
const [error, submitAction, isPending] = useActionState(
  async (prevState, formData) => {
    const error = await updateName(formData.get("name"));
    if (error) {
      return error;
    }
    redirect("/path");
    return null;
  },
  null,
);
  • 위 코드에서 submitAction은 폼 제출에 사용되고, error와 isPending은 자동으로 업데이트된다.

3. <form>와 useFormStatus

3.1 <form> action={함수} 사용

  • React 19에서는 <form> 태그의 action, formAction에 함수를 직접 전달할 수 있다.
  • 제출 시 자동으로 대기 상태를 관리해주고, 성공 시 폼이 초기화된다.
<form action={submitAction}>
  <input type="text" name="name" />
  <button type="submit" disabled={isPending}>Update</button>
  {error && <p>{error}</p>}
</form>

3.2 useFormStatus 훅

  • useFormStatus를 통해 부모 폼의 상태를 쉽게 조회할 수 있다.
  • 디자인 시스템 같은 곳에서 별도 Context를 정의하지 않고도, 현재 폼의 대기 상태 등을 가져올 수 있다.
import { useFormStatus } from 'react-dom';

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

4. useOptimistic 훅

  • 낙관적 업데이트(Optimistic Update)를 손쉽게 구현하기 위한 훅이다.
  • 서버 응답 전이라도 UI 상태를 미리 갱신하고, 오류 발생 시 되돌리는 로직을 간단히 처리한다.
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" />
      </p>
    </form>
  );
}

5. 새 API: use

  • 렌더링 중에 프로미스Context를 읽고, 자동으로 서스펜스(Suspense) 처리할 수 있도록 해주는 API다.
  • 예: use(commentsPromise)로 호출 시, 프로미스가 완료될 때까지 UI를 서스펜스 처리한다.
import { use } from 'react';

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);  // 프로미스 완료 전까지 Suspense
  return comments.map((c) => <p key={c.id}>{c.text}</p>);
}
  • 또, use(ThemeContext)처럼 조건부로 Context를 사용하는 것도 지원한다.
  • 다만, 렌더링 도중 새로 만든 프로미스는 지원하지 않는다.

6. React DOM Static API

  • react-dom/static에 prerender, prerenderToNodeStream가 추가되어, 데이터 로딩이 완료된 정적 HTML을 생성한다.
  • 기존 renderToString와 달리, 모든 비동기 로딩이 끝날 때까지 기다린 후 HTML을 반환하는 방식이다.
import { prerender } from 'react-dom/static';

async function handler(request) {
  const { prelude } = await prerender(<App />, {
    bootstrapScripts: ['/main.js'],
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

7. React Server Components

  • 서버 컴포넌트(Server Components)는 빌드 시점이나 웹 서버에서 미리 컴포넌트를 렌더링하는 방식을 제공한다.
  • 클라이언트 측의 번들 크기를 줄이고, 서버 자원을 활용해 렌더링 성능을 높일 수 있다.

8. Server Actions

  • "use server" 지시어를 통해 클라이언트 컴포넌트가 서버 함수를 직접 호출할 수 있게 해준다.
  • 함수가 클라이언트에서 실행되는 대신, React가 자동으로 서버로 요청을 보내고 결과를 가져온다.
// server.js
"use server";

export async function saveDataOnServer(data) {
  // 서버 로직
  return await someDBCall(data);
}

// client.js
import { saveDataOnServer } from './server.js';

function ClientComponent() {
  const handleClick = async () => {
    const result = await saveDataOnServer({ foo: 'bar' });
    console.log(result);
  };

  return <button onClick={handleClick}>Save</button>;
}

9. ref를 prop으로 사용

  • 함수형 컴포넌트에서 ref를 직접 prop으로 받을 수 있게 되었다.
  • 기존 forwardRef 없이도 DOM 요소나 컴포넌트 인스턴스에 접근 가능하다.
function MyInput({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

// 사용 예시
<MyInput ref={inputRef} placeholder="Enter text" />

10. 하이드레이션 오류 개선

  • 서버 렌더링된 내용과 클라이언트 렌더링이 불일치할 때, 기존에는 여러 번 경고가 쏟아졌으나 이제 단 한 번에 명확히 알려준다.
  • 콘솔에 mismatch 부분을 diff 형태로 표기해 디버깅을 간단히 한다.

11. <Context> 자체를 Provider로 사용

  • <Context.Provider> 대신 <Context> 태그를 직접 사용할 수 있다.
const ThemeContext = createContext('');

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

12. Ref 콜백의 정리(cleanup) 함수

  • Ref 콜백에서 반환값으로 정리 함수를 반환하면, 언마운트 시 자동으로 호출된다.
  • 기존에는 언마운트 시 ref 콜백이 null로 재호출되는 방식이었으나, 이제 더 명확하게 cleanup 로직을 작성할 수 있다.
<input
  ref={(el) => {
    // 생성 로직
    return () => {
      // cleanup 로직
    };
  }}
/>

 


13. useDeferredValue 초기값

  • useDeferredValue(value, initialValue) 형태로 초기값을 지정할 수 있다.
  • 첫 렌더에서 initialValue를 사용하고, 이후에 비동기로 value를 반영한다.
function Search({ deferredInput }) {
  const value = useDeferredValue(deferredInput, '');
  return <Results query={value} />;
}

14. 문서 메타데이터 지원

  • 컴포넌트에서 <title>, <meta>, <link> 태그를 직접 작성하면, 자동으로 <head> 영역으로 호이스팅된다.
  • 서버 렌더링, 스트리밍 환경에서도 일관된 메타데이터 관리가 가능하다.
function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Author Name" />
      <link rel="canonical" href={`/posts/${post.id}`} />
      <p>...</p>
    </article>
  );
}

15. 스타일시트 관리

  • <link rel="stylesheet">나 <style> 요소를 컴포넌트 단위로 작성해도, React가 중복 로드 없이 알맞은 순서로 DOM에 삽입한다.
  • precedence 속성을 이용해 스타일 우선순위를 지정할 수 있다.
function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo.css" precedence="default" />
      <link rel="stylesheet" href="bar.css" precedence="high" />
      <div className="foo-class bar-class">...</div>
    </Suspense>
  );
}

16. 비동기 스크립트(async)

  • <script async>를 여러 컴포넌트에서 렌더링해도 한 번만 로드되고 실행된다.
  • 서버 렌더링 시 <head>로 모아서, 중요 자원(스타일, 폰트 등)보다 뒤에 로드하도록 우선순위를 조정한다.
function MyComponent() {
  return (
    <div>
      <script async src="myScript.js" />
      Content...
    </div>
  );
}

17. 리소스 사전 로드

  • prefetchDNS, preconnect, preload, preinit 등을 사용해 브라우저가 필요한 자원을 미리 가져오도록 지시할 수 있다.
  • 초기 페이지 로딩 성능 개선이나 예상되는 네비게이션 시 미리 로딩하는 데 유용하다.
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function MyComponent() {
  preconnect('https://cdn.example.com');
  preload('https://cdn.example.com/font.woff', { as: 'font' });
  preinit('https://cdn.example.com/main.js', { as: 'script' });
  return <div>Content...</div>;
}

18. 커스텀 엘리먼트 지원

  • 커스텀 엘리먼트를 사용할 때, 서버 렌더링 시 원시 타입(props)만 속성으로 렌더링하고, 클라이언트에서는 프로퍼티로 할당한다.
  • CustomElement 관련 호환성이 크게 향상되었다.
<my-custom-element someProp={123} onChange={handleChange} />

 

 

출처 : https://react.dev/blog/2024/12/05/react-19