홍승아블로그

react v19

Overview

React 19는 개발자 경험을 향상시키고, 일반적인 패턴을 간소화하며 성능을 개선하는 여러 새로운 기능과 개선 사항을 도입했다. 아래는 주요 변경 사항과 코드 예제를 포함한 요약이다.

What’s new in React 19

Actions

React 앱에서 일반적인 사용 사례는 데이터 변형을 수행한 다음 응답에서 상태를 업데이트 진행하였습니다.

단, 과거에는 pedding, state, optimistic updates 를 순차적으로 상태로 관리하였지만, Actions 도입되면서 자동으로 처리가 가능합니다.

  • 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>
      );
    }
  • Support for using async functions

    // 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>
      );
    }
  • useActionState: Handle common cases for Actions

    • 이전 Canary 버전에 useFormState 네이밍이 useActionState로 변경
    // 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 DOM: 

     Actions React 19에서는 <form><input><button> 요소의 action과 formAction 속성에 함수를 전달할 수 있다

    <form action={actionFunction}>
    + <button formAction={formAction}>Increment</button>
    + <input type="submit" formAction={formAction}>Increment</input>
  • useFormStatus: React DOM: New hook 컴포넌트 drilling props없이 폼의 대한 정보를 접근할 수 있는 hooks

    import { useFormStatus } from 'react-dom';
    import action from './actions';
    
    function Submit() {
      const status = useFormStatus();
      return <button disabled={status.pending}>Submit</button>;
    }
    
    export default function App() {
      return (
        <form action={action}>
          <Submit />
        </form>
      );
    }
  • useOptimistic: New hook 비동기 요청이 진행되는 동안 최종 상태를 낙관적으로 보여주기 위한 hook

    function ChangeName({ currentName, onUpdateName }) {
      const [optimisticName, setOptimisticName] = useOptimistic(currentName); // initial state
    
      const submitAction = async formData => {
        const newName = formData.get('name');
        setOptimisticName(newName); // set state
        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>
      );
    }

USE

  • useContext → use

    • useContext 사용법은 동일하나, 조건문에서도 사용이 가능한 것이 특징입니다.
    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>;
    }
  • promise → use

    • promise를 use로 감싸고 상위에 Suspense로 감싸서 사용하면 됩니다.
    • 아래 코드를 보시다시피 간결하게 코드를 작성할 수 있으므로, 기존에 사용하던 비동기 라이브러리를 대체가 가능할 것으로 보입니다.
    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>
      );
    }
    • 그외 공유사항

React Server Components와 Server Actions

  • Server Components 클라이언트 애플리케이션이나 SSR 서버와 별도의 환경에서 컴포넌트를 미리 렌더링할 수 있는 옵션입니다. 단, 오해하는 것은 use server 지시어는 server actions에서만 사용하는 것이지 서버 컴포넌트에서 사용하는게 아닙니다.
  • Server Actions 클라이언트 컴포넌트에서 서버에서 실행되는 비동기 함수를 호출할 수 있습니다.

Improvements in React 19

  • forwardRef → ref is a prop

    • forwardRef 감싸서 ref 넘기는 문법에서 ref를 그대로 prop로 넘기도록 변경
    function MyInput({ placeholder, ref }) {
      return <input placeholder={placeholder} ref={ref} />;
    }
    
    //...
    <MyInput ref={ref} />;
  • improved error reporting for hydration errors in react-dom hydration 오류 보고에 대한 개선, 불일치에 대한 비교와 단일 메시지로 변경

    • 개선 전 image.png
    • 개선 후 image.png
  •  as a provider Context.Provider 사용하는 대신 Context 사용하는 것으로 대체되었다.

    // < v19
    const ThemeContext = createContext('');
    
    function App({ children }) {
      return (
        <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
      );
    }
    
    // >= v19
    // < v19
    const ThemeContext = createContext('');
    
    function App({ children }) {
      return <ThemeContext value="dark">{children}</ThemeContext>;
    }
  • Cleanup functions for refs 컴포넌트가 unmount 될 때, ref에 cleanup function이 호출하는 기능이 추가되었습니다.

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

    cleanup function이 도입되면서 ref콜백에서 다른 것을 반환하는 것은 이제 TypeScript 에러가 발생함으로, 해결을 위해서는 반환을 하지 않도록 처리를 해야합니다.

    -(<div ref={current => (instance = current)} />) +
    (
      <div
        ref={current => {
          instance = current;
        }}
      />
    );
  • useDeferredValue initial value useDeferredValue에 초기값 세팅이 가능해졌습니다.

    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} />;
    }

Support for Document Metadata

  • Overview 구성 요소 트리의 어느 곳에서나 <title><meta> 및 metadata <link> 태그 렌더링에 대한 내장 지원을 추가했습니다. 서버/클라이언트 컴포넌트 모두 지원하며, 기존에 사용중이던 React Helmet과 같은 라이브러리가 개척한 기능에 대해서 내재화를 뜻합니다. 단, 현재 경로에 따라 일반 메타데이터를 특정 메타데이터로 재정의를 해야할 경우에는 React Helmet과 같은 라이브러리를 여전히 사용해야 합니다.
  • Example
    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>
      );
    }

Support for stylesheets

  • Overview 스타일시트에 대한 기본 지원을 제공하며, precedence 스타일 속성을 통해서 삽입 우선 순위를 정할 수 있습니다. 클라이언트 사이드 렌더링 커밋 전에 추가된 스타일 시트를 기다렸다가 우선순위에 맞게 로드를 할 수 있도록 처리해줍니다.

  • Example

    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>
      )
    }

Support for async scripts

  • Example

    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>
    }

Support for preloading resources

  • Overview 최초 문서 로드 시와 클라이언트 측 업데이트 시, 브라우저에 가능한 한 빨리 로드해야 할 리소스에 대해 알려주는 기능이 추가되었습니다.

  • Example

    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
    }
    
    // result
    <!-- 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>
    
    • prefetchDNS : 연결하려는 DNS 도메인 이름의 IP 주소를 미리 가져온다.

      import { prefetchDNS } from 'react-dom';
      
      function AppRoot() {
        prefetchDNS('https://example.com');
        // ...
      }
    • preconnect 리소스를 요청할 것으로 예상되는 서버에 연결할 때 사용한다.

      import { preconnect } from 'react-dom';
      
      function AppRoot() {
        preconnect('https://example.com');
        // ...
      }
    • preload 사용할 것으로 예상되는 스타일시트, 글꼴, 이미지 또는 외부 스크립트를 가져 올 때 사용한다.

      import { preload } from 'react-dom';
      
      function AppRoot() {
        preload('https://example.com/font.woff2', { as: 'font' });
        // ...
      }
    • preloadModule 사용하려는 ESM 모듈을 가져올 수 있습니다.

      import { preloadModule } from 'react-dom';
      
      function AppRoot() {
        preloadModule('https://example.com/module.js', { as: 'script' });
        // ...
      }
    • preinit 스크립트나 스타일시트를 사전 초기화할 경우 사용된다.

      import { preinit } from 'react-dom';
      
      function AppRoot() {
        preinit('https://example.com/script.js', { as: 'script' });
        // ...
      }
    • preinitModule ESM 모듈을 사전 초기화 할 경우 사용된다.

      import { preinitModule } from 'react-dom';
      
      function AppRoot() {
        preinitModule('https://example.com/module.js', { as: 'script' });
        // ...
      }

Migration Guide

Installing

npm install --save-exact react@latest react-dom@latest
npm install -D @types/react@latest @types/react-dom@latest

yarn add --exact react@latest react-dom@latest
yarn add -D @types/react@latest @types/react-dom@latest

## Codemods

```bash
npx codemod@latest react/19/migration-recipe

Errors in render are not re-thrown

React 19에서는 에러 핸들링을 세밀하게 제어할 수 있는 새로운 기능이 도입되었습니다.

이제 createRoot와 hydrateRoot 메서드에서 onCaughtError와 onUncaughtError 옵션을 사용하여 에러 처리 방식을 조정할 수 있습니다.

기존의 문제점

기존에는 Error Boundary를 사용하여 에러를 잡았지만, 여전히 콘솔에 console.error로 에러가 출력되는 문제가 있었습니다.

이로 인해 개발자는 에러가 정상적으로 처리되었음에도 불구하고 불필요한 에러 메시지를 보게 되었고, 이는 개발 과정에서 혼란을 초래할 수 있었습니다.

React 19 개선점

React 19에서는 에러 핸들링을 더 세밀하게 제어할 수 있는 방법이 제공됩니다.

createRoot와 hydrateRoot의 onCaughtError 및 onUncaughtError 옵션을 사용하여 콘솔에 에러 메시지를 출력하는 기본 동작을 덮어쓸 수 있습니다.

onCaughtError는 Error Boundary가 잡은 에러를 처리할 때 사용됩니다.

const root = createRoot(container, {
  onUncaughtError: (error, errorInfo) => {
    // ... log error report(Errors that are not caught by an Error Boundary)
  },
  onCaughtError: (error, errorInfo) => {
    // ... log error report(Errors that are caught by an Error Boundary)
  },
});

Removed deprecated React APIs

  • Removed: propTypes and defaultProps for functions ProtoTypes는 2017년 4월에 미지원 처리 되었습니다. 타입 사용을 계속 지원하고 싶다면 typescript나 타입 솔류션 패키지를 사용하시면 됩니다. ES6에 default parameter 지원이 될 경우에는 defaultProps 또한 제거 가능(단, class 형 컴포넌트에서는 계속적인 지원예정)

    // < 19
    import PropTypes from 'prop-types';
    
    function Heading({ text }) {
      return <h1>{text}</h1>;
    }
    Heading.propTypes = {
      text: PropTypes.string,
    };
    Heading.defaultProps = {
      text: 'Hello, world!',
    };
    
    // >= 19
    // After
    interface Props {
      text?: string;
    }
    function Heading({ text = 'Hello, world!' }: Props) {
      return <h1>{text}</h1>;
    }
  • Legacy Context using contextTypes and getChildContext Legact Context는 2018년 10월에 미지원 처리 되었습니다 대신 계속적으로 클래스형 컴포넌트에서 계속적으로 context Type을 사용하고 싶으시다면 contextType API를 사용해주세요.

    // < 19
    import PropTypes from 'prop-types';
    
    class Parent extends React.Component {
      static childContextTypes = {
        foo: PropTypes.string.isRequired,
      };
    
      getChildContext() {
        return { foo: 'bar' };
      }
    
      render() {
        return <Child />;
      }
    }
    
    class Child extends React.Component {
      static contextTypes = {
        foo: PropTypes.string.isRequired,
      };
    
      render() {
        return <div>{this.context.foo}</div>;
      }
    }
    
    // >= 19
    const FooContext = React.createContext();
    
    class Parent extends React.Component {
      render() {
        return (
          <FooContext value="bar">
            <Child />
          </FooContext>
        );
      }
    }
    
    class Child extends React.Component {
      static contextType = FooContext;
    
      render() {
        return <div>{this.context}</div>;
      }
    }
  • Removed: string refs string refs는 2018년 3월에 미지원 처리 되었습니다. ref 콜백 지원 전에 refs 문자열을 지원했었지만, react 19에서는 리액트를 더 간단하고 이해하기 쉽게 하기 위해서 문자열 refs 방식을 제거합니다. 혹여 사용된 코드가 있다면 ref 콜백 방식으로 변경해주셔야 합니다. codemod: npx codemod@latest react/19/replace-string-ref

    // < 19
    class MyComponent extends React.Component {
      componentDidMount() {
        this.refs.input.focus();
      }
    
      render() {
        return <input ref="input" />;
      }
    }
    
    // >= 19
    class MyComponent extends React.Component {
      componentDidMount() {
        this.input.focus();
      }
    
      render() {
        return <input ref={input => (this.input = input)} />;
      }
    }
  • Removed: Module pattern factories Module pattern factories는 2019년 8월에 미지원 처리 되었습니다. React 19에서는 Module pattern factories 지원 제거됨으로써, 일반 함수로 마이그레이션을 해주셔야합니다.

    // < 19
    function FactoryComponent() {
      return {
        render() {
          return <div />;
        },
      };
    }
    
    // >= 19
    function FactoryComponent() {
      return <div />;
    }
  • Removed: React.createFactory createFactory는 2020년 2월에 미지원 처리 되었습니다. React 19에서 제거됨으로써, 일반 JSX로 마이그레이션을 해주셔야합니다.

    // < 19
    import { createFactory } from 'react';
    
    const button = createFactory('button');
    
    // >= 19
    const button = <button />;
  • Removed: react-test-renderer/shallow React 18에서는 react-shallow-renderer → react-test-renderer/shallow re-export 처리해줬으나, React 19에서는 react-test-renderer/shallow 삭제됨으로써 직접 react-shallow-renderer 패키지를 설치해주셔야합니다.

    npm install react-shallow-renderer --save-dev
    
    - import ShallowRenderer from 'react-test-renderer/shallow';
    + import ShallowRenderer from 'react-shallow-renderer';
  • Removed deprecated React DOM APIs 기존 act 메서드를 react-dom/test-utils 가져오는 것을 react에서 가져오도록 변경되었습니다.(test-utils 제거) codemod: npx codemod@latest react/19/replace-act-import

    - import {act} from 'react-dom/test-utils'
    + import {act} from 'react';
  • Removed: ReactDOM.render, ReactDOM.hydrate ReactDOM.render, ReactDOM.hydrate 는 2022년 3월에 미지원 처리 되었습니다. ReactDOM.render → ReactDOM.createRoot ReactDOM.hydrate → ReactDOM.hydrateRoot codemod: npx codemod@latest react/19/replace-reactdom-render

    // < 19
    import { render } from 'react-dom';
    render(<App />, document.getElementById('root'));
    
    // >= 19
    import { createRoot } from 'react-dom/client';
    const root = createRoot(document.getElementById('root'));
    root.render(<App />);
    
    // < 19
    import { hydrate } from 'react-dom';
    hydrate(<App />, document.getElementById('root'));
    
    // >= 19
    import { hydrateRoot } from 'react-dom/client';
    hydrateRoot(document.getElementById('root'), <App />);
  • Removed: unmountComponentAtNode ReactDOM.unmountComponentAtNode는 2022년 3월에 미지원 처리되었습니다. unmountComponentAtNode → root.unmount

    // < 19
    unmountComponentAtNode(document.getElementById('root'));
    
    // >= 19
    const root = createRoot(document.getElementById('root'));
    root.unmount();
  • Removed: ReactDOM.findDOMNode ReactDOM.findDOMNode는 2018년 10월에 미지원 처리되었습니다. findDOMNode 삭제됨에 따라서 Dom Refs로 마이그레이션을 해주셔야합니다.

    // < 19
    import { findDOMNode } from 'react-dom';
    
    function AutoselectingInput() {
      useEffect(() => {
        const input = findDOMNode(this);
        input.select();
      }, []);
    
      return <input defaultValue="Hello" />;
    }
    
    // >= 19
    function AutoselectingInput() {
      const ref = useRef(null);
      useEffect(() => {
        ref.current.select();
      }, []);
    
      return <input ref={ref} defaultValue="Hello" />;
    }

New deprecations

  • Deprecated: element.ref react 19에서 ref를 props로 지원함에 따라서 기존에 element.ref 지원하지 않고 접근하시려면 element.props.ref로 접근을 해주셔야합니다. element.ref 접근 시 경고 image.png

  • Deprecated: react-test-renderer react-test-renderer → @testing-library/react or @testing-library/react-native

  • Notable changes StrictMode changes Improvements to Suspense UMD builds removed Libraries depending on React internals may block upgrades

  • TypeScript changes React 19 API 제거 됨에 따라 제거된 API 연관된 타입 또한 제거되었습니다. codemod: types-react-codemod

    npx types-react-codemod@latest ./path-to-app
    
    npx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files
  • ref cleanups required 컴포넌트가 unmount 될 때, ref에 cleanup function이 호출하는 기능이 추가되었습니다. 컴포넌트가 unmount 될 때, ref에 cleanup function이 호출하는 기능이 추가되었습니다.

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

    cleanup function이 도입되면서 ref콜백에서 다른 것을 반환하는 것은 이제 TypeScript 에러가 발생함으로, 해결을 위해서는 반환을 하지 않도록 처리를 해야합니다.

    -(<div ref={current => (instance = current)} />) +
    (
      <div
        ref={current => {
          instance = current;
        }}
      />
    );
  • useRef requires an argument useRef, createContext에 꼭 인자를 넣도록 수정 null → RefObject

    • 리턴 타입은 RefObject 로 ref 객체의 .current 프로퍼티 값을 직접 변경 불가능
    • 단, ref 객체에 얉은 복사 임으로 하위 속성은 변경 가능 그 외(undefined 포함)
    • MutableRefObject<T | undefined>로 ref 객체의 .current 프로퍼티를 직접 변경가능
    // @ts-expect-error: Expected 1 argument but saw none
    useRef();
    // Passes
    useRef(undefined);
    // @ts-expect-error: Expected 1 argument but saw none
    createContext();
    // Passes
    createContext(undefined);
    
    const ref = useRef<number>(null);
    
    // Cannot assign to 'current' because it is a read-only property
    ref.current = 1;
  • Changes to the ReactElement TypeScript type ReactElement 엘리먼트 타입이 기존에 any에서 unknown으로 변경되었습니다.

    type Example = ReactElement['props'];
    //   ^? Before, was 'any', now 'unknown'
  • The JSX namespace in TypeScript

  • Better useReducer typings useReducer 타입 추론 기능이 개선되었습니다. 기존에 타입을 지정했던 방식에서 타입 추론을 최대한 할 수 있는 방식으로 개선되었습니다.

    -useReducer<React.Reducer<State, Action>>(reducer) +
      useReducer(reducer) -
      useReducer<React.Reducer<State, Action>>((state, action) => state) +
      useReducer((state: State, action: Action) => state);

REACT Compiler

  • Overview

  • 컴파일러는 무엇을 하나요?

  • 컴파일러 초기 가정

    • 올바르고 의미 있는 JavaScript 코드로 작성
    • nullable/optional 값과 속성에 접근하기 전에 그 값이 정의되어 있는지 테스트
      • TypeScript 사용하는 경우 strictNullChecks 활성화 후 수행
    • React의 규칙을 잘 키지는지
  • 호환성 확인

    npx react-compiler-healthcheck@experimental
    
    Successfully compiled 8 out of 9 components.
    // 성공적으로 최적화할 수 있는 컴포넌트 수 확인: 숫자가 높을수록 좋습니다
    
    StrictMode usage not found.
    // <StrictMode> 사용 여부 확인: 이를 활성화하고 준수할 경우 React의 규칙을 잘 따르는 가능성이 높습니다.
    
    Found no usage of incompatible libraries.
    // 호환되지 않는 라이브러리 사용 여부 확인: 알려진 라이브러리 중에서 컴파일러와 호환되지 않는 라이브러리를 확인합니다.
  • eslint-plugin-react-compiler(기존 코드 베이스 적용) React 규칙 위반 사항을 표시 compilationMode: “annotation” 모드 사용 시 컴파일 진행 시 “use memo” 지시어를 추가할 경우에만 컴파일이 진행됩니다.

    // 호환성 확인
    npx react-compiler-healthcheck@experimental
    
    // .eslint
    module.exports = {
      plugins: [
        'eslint-plugin-react-compiler',
      ],
      rules: {
        'react-compiler/react-compiler': "error",
      },
    }
    
    const ReactCompilerConfig = {
      compilationMode: "annotation",
    };
    
    // src/app.jsx
    export default function App() {
      "use memo";
      // ...
    }
  • babel-plugin-react-compiler@experimental(전체 코드 베이스 적용) babel-plugin-react-compiler는 다른 Babel 플러그인보다 먼저 실행되야 합니다.

    • 설치
    
    npm install babel-plugin-react-compiler@experimental
    • babel.config.js
    // babel.config.js
    const ReactCompilerConfig = {
      /* ... */
    };
    
    module.exports = function () {
      return {
        plugins: [
          ['babel-plugin-react-compiler', ReactCompilerConfig], // 가장 먼저 실행하세요!
          // ...
        ],
      };
    };
    • React 19 RC 버전을 설치가 어려울 경우 리액트 버전을 명시가 가능(단, react-compiler-runtime 패키지 설치해서 진행)
    // 설치
    npm install react-compiler-runtime@experimental
    
    // babel.config.js
    const ReactCompilerConfig = {
      target: '18' // '17' | '18' | '19'
    };
    
    module.exports = function () {
      return {
        plugins: [
          ['babel-plugin-react-compiler', ReactCompilerConfig],
        ],
      };
    };
  • vite 적용

    // vite.config.js
    const ReactCompilerConfig = {
      /* ... */
    };
    
    export default defineConfig(() => {
      return {
        plugins: [
          react({
            babel: {
              plugins: [['babel-plugin-react-compiler', ReactCompilerConfig]],
            },
          }),
        ],
        // ...
      };
    });

참고페이지

다음글
nextjs 15