React.js 지식 - React.js 19 배타, 무엇이 달라졌는가

이유승·2024년 4월 28일
0

React.js 지식

목록 보기
7/8

React 19 Beta

React 19 베타 버전이 npm을 통해 사용 가능합니다. 이 버전은 주로 라이브러리 개발자들이 React 19에 대비할 수 있도록 하기 위한 것입니다. 앱 개발자들은 안정적인 React 19가 출시될 때까지 React 18.3.0을 사용하는 것이 좋습니다.

React 19의 새로운 기능들

Actions:

데이터 변형 후 상태 업데이트를 쉽게 처리할 수 있도록 useTransition과 같은 기능이 도입되었습니다.
예를 들어, 이름 업데이트와 같은 API 요청 후 자동으로 처리되는 에러 상태와 펜딩 상태를 관리할 수 있습니다.

useOptimistic:

비동기 요청이 진행 중일 때 최적의 상태를 즉시 보여주고, 업데이트가 완료되거나 오류가 발생하면 원래 상태로 자동으로 돌아갑니다.

useActionState:

Actions를 사용하여 데이터 제출 과정을 자동으로 관리하고, 폼 요소에 함수를 전달할 수 있는 기능을 포함하고 있습니다.<form>, <input>, <button> 요소에 함수를 actionformAction 속성으로 전달하여 폼을 자동으로 제출할 수 있습니다.

useFormStatus:

폼의 상태를 컨텍스트 없이 직접 액세스할 수 있게 하는 새로운 훅으로, 디자인 시스템에서 유용하게 사용될 수 있습니다.

use:

use API를 통해 리소스를 렌더링 중에 읽을 수 있습니다. 예를 들어, 프로미스를 사용하여 비동기 데이터를 처리할 때까지 렌더링을 중단할 수 있습니다.

서버 컴포넌트:

빌드 시간에 미리 컴포넌트를 렌더링하거나 각 요청에 따라 별도의 서버 환경에서 실행할 수 있는 새로운 옵션입니다.

Server Actions:

클라이언트 컴포넌트가 서버에서 실행되는 비동기 함수를 호출할 수 있게 합니다.

React 19에서 개선된 점들

ref를 prop으로 사용:

함수 컴포넌트에서 ref를 prop으로 직접 접근할 수 있게 되었습니다.

수화 오류 보고 개선:

클라이언트와 서버 사이의 HTML 불일치를 보다 명확하게 식별하고 보고합니다.

Context 프로바이더 간소화:

<Context.Provider> 대신 를 직접 사용하여 프로바이더를 렌더링할 수 있습니다.

ref 콜백에서 클린업 함수 지원:

컴포넌트가 DOM에서 제거될 때 클린업 함수를 반환하여 ref를 초기화할 수 있습니다.

스타일시트 및 스크립트 지원 개선

내부적으로 스타일시트와 스크립트 태그를 더 적절하게 처리하여, 컴포넌트 로딩 시 스타일과 스크립트의 종속성을 관리합니다.

리소스 사전 로딩

초기 페이지 로드 및 클라이언트 사이드 업데이트를 최적화하기 위해 새로운 API를 도입하여 브라우저가 필요한 리소스를 더 빨리 인식하고 로드할 수 있게 합니다.

React 19 업그레이드 방법

업그레이드 가이드를 참조하여 새로운 기능들을 단계별로 적용할 수 있습니다.

  • 이상의 내용은 React 공식 블로그의 React 19 beta의 이해를 위해 요약 정리한 내용입니다. 번역이나 내용 파악이 잘못될 가능성이 매우 높으니 반드시 원 글의 내용을 참조해주세요.





1. 추가 정리 - React 19의 새로운 기능들

  • 아래 내용은 위 요약 정리를 바탕으로 어떤 부분이 어떻게 변화했는지를 이해하기 위해 Chat GPT를 이용하여 예시 코드를 뽑아본 것입니다. 제가 일개 취준생에 불과한지라 이 내용이 얼마나 정확한지 파악하는데 어려움을 겪고 있습니다. 따라서 아래 내용의 정확도는 보장할 수 없으니 여기서부터는 무시하셔도 됩니다.

Actions과 useTransition 사용

변경 전:

이전에는 사용자가 이름을 변경하는 폼을 제출했을 때, 비동기 요청을 처리하고 결과에 따라 상태를 업데이트하는 과정에서 많은 상태 관리가 필요했습니다.

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

  const handleSubmit = async () => {
    setIsPending(true);
    try {
      await updateName(name);
      redirect("/success");
    } catch (error) {
      setError(error);
    }
    setIsPending(false);
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>Update Name</button>
      {error && <p>Error: {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 () => {
      try {
        await updateName(name);
        redirect("/success");
      } catch (error) {
        setError(error);
      }
    });
  };

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

3. useActionState

변경 전:

비동기 요청을 처리하고 폼을 제출하는 과정을 직접 구현해야 했습니다.

function UpdateProfile() {
  const [formData, setFormData] = useState({ name: "", email: "" });
  const [error, setError] = useState(null);

  const handleSubmit = async (event) => {
    event.preventDefault();
    try {
      await updateProfile(formData);
      // handle success
    } catch (error) {
      setError(error.toString());
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.name}
        onChange={e => setFormData({ ...formData, name: e.target.value })}
        placeholder="Name"
      />
      <input
        value={formData.email}
        onChange={e => setFormData({ ...formData, email: e.target.value })}
        placeholder="Email"
      />
      <button type="submit">Update Profile</button>
      {error && <p>{error}</p>}
    </form>
  );
}

변경 후:

useActionState을 사용하여 데이터 제출 과정을 간소화하고, 폼의 상태 관리를 자동화합니다.

function UpdateProfile() {
  const [formData, setFormData] = useState({ name: "", email: "" });
  const [error, submitAction, isPending] = useActionState(async (prevState) => {
    try {
      await updateProfile(formData);
      // handle success
    } catch (error) {
      return error.toString(); // Return error for useActionState to handle
    }
  });

  return (
    <form action={submitAction}>
      <input
        name="name"
        defaultValue={formData.name}
        onChange={e => setFormData({ ...formData, name: e.target.value })}
        placeholder="Name"
      />
      <input
        name="email"
        defaultValue={formData.email}
        onChange={e => setFormData({ ...formData, email: e.target.value })}
        placeholder="Email"
      />
      <button type="submit" disabled={isPending}>Update Profile</button>
      {error && <p>{error}</p>}
    </form>
  );
}

4. useFormStatus

변경 전:

폼의 상태를 관리하기 위해 상위 컴포넌트에서 props를 통해 상태를 전달해야 했습니다.

function Form() {
  const [isSubmitting, setIsSubmitting] = useState(false);

  return (
    <div>
      <SubmitButton isSubmitting={isSubmitting} />
    </div>
  );
}

function SubmitButton({ isSubmitting }) {
  return <button disabled={isSubmitting}>Submit</button>;
}

변경 후:

useFormStatus를 사용하여 폼의 상태를 직접 액세스하고, 상태 전달 없이 컴포넌트를 단순화합니다.

function Form() {
  return (
    <form>
      <SubmitButton />
    </form>
  );
}

function SubmitButton() {
  const { isPending } = useFormStatus(); // Directly access form's pending status
  return <button disabled={isPending}>Submit</button>;
}

이러한 변화들을 통해 React 19는 개발자가 비동기 상태 관리와 폼 제출을 더 간편하게 처리할 수 있도록 지원하며, 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.

5. use API

변경 전:

React에서 비동기 데이터를 처리할 때 주로 Suspense와 useEffect를 사용해왔습니다.

function Comments({ commentsPromise }) {
  const [comments, setComments] = useState([]);

  useEffect(() => {
    commentsPromise.then(setComments);
  }, [commentsPromise]);

  return (
    <div>
      {comments.map(comment => <p key={comment.id}>{comment.text}</p>)}
    </div>
  );
}

변경 후:

use API를 사용하여 비동기 리소스를 더 직관적으로 처리할 수 있습니다. 이는 비동기 데이터를 직접적으로 '읽을' 수 있게 해서 Suspense와 연동됩니다.

import { use } from 'react';

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);

  return (
    <div>
      {comments.map(comment => <p key={comment.id}>{comment.text}</p>)}
    </div>
  );
}

이 코드에서 use는 commentsPromise가 해결될 때까지 컴포넌트의 렌더링을 중단하고, 데이터가 준비되면 자동으로 계속됩니다. 이는 컴포넌트 내에서 비동기 데이터를 더 선언적이고 효율적으로 처리할 수 있게 합니다.

6. useOptimistic

변경 전:

사용자가 데이터를 변경하는 요청을 보낼 때까지 변경사항을 보지 못하는 구조였습니다.

function Profile({ userID }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(userID).then(setUser);
  }, [userID]);

  function handleNameChange(newName) {
    setUser(prev => ({ ...prev, name: newName }));
    updateUserName(userID, newName);
  }

  return (
    <div>
      <input value={user ? user.name : ''} onChange={(e) => handleNameChange(e.target.value)} />
    </div>
  );
}

변경 후:

useOptimistic을 사용하여 사용자의 입력을 즉시 반영하고 서버 응답을 기다리면서 최적화된 상태를 유지할 수 있습니다.

function Profile({ userID }) {
  const [user, setUser] = useOptimistic(null);

  useEffect(() => {
    fetchUser(userID).then(data => setUser(data));
  }, [userID]);

  function handleNameChange(newName) {
    setUser(prev => ({ ...prev, name: newName }));
    updateUserName(userID, newName).catch(() => {
      // 롤백 또는 에러 처리
    });
  }

  return (
    <div>
      <input value={user ? user.name : ''} onChange={(e) => handleNameChange(e.target.value)} />
    </div>
  );
}

이 예시에서 useOptimistic은 사용자 인터페이스를 최신 상태로 유지하면서 백그라운드에서 비동기 작업을 처리하도록 도와줍니다. 사용자가 입력한 데이터는 즉시 반영되어 사용자 경험을 크게 개선합니다.



2. 추가 정리 - React 19의 개선된 점들

1. ref를 prop으로 사용

변경 전:

함수 컴포넌트에서 ref를 사용하려면 React.forwardRef를 사용하여 ref를 명시적으로 전달해야 했습니다.

const MyInput = React.forwardRef((props, ref) => (
  <input ref={ref} {...props} />
));

function App() {
  const inputRef = useRef();
  return <MyInput ref={inputRef} placeholder="Enter text here" />;
}

변경 후:

React 19에서는 함수 컴포넌트에 직접 ref를 prop으로 전달할 수 있어, forwardRef를 사용할 필요가 없어졌습니다.

function MyInput({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

function App() {
  const inputRef = useRef();
  return <MyInput ref={inputRef} placeholder="Enter text here" />;
}

이 변경으로 코드가 더 간결해지고, 컴포넌트의 사용성이 향상되었습니다.

2. 수화 오류 보고 개선

변경 전:

수화(hydration) 과정에서 발생하는 HTML 불일치 오류는 명확한 정보 없이 다수의 오류로 보고되었습니다.

// 예시: 오류 메시지가 구체적이지 않고 사용자가 문제를 파악하기 어려웠습니다.
console.error("Warning: Text content did not match.");

변경 후:

React 19에서는 HTML 불일치가 발생했을 때 상세한 오류 메시지와 함께 불일치 부분을 명확하게 보고합니다.

// 예시: 오류 메시지가 더 상세하고, 어디에서 불일치가 발생했는지를 정확히 알 수 있습니다.
console.error("Hydration mismatch: Server: 'Hello' Client: 'Hallo'");

이로 인해 개발자는 수화 오류를 더 쉽게 식별하고 수정할 수 있습니다.

3. Context 프로바이더 간소화

변경 전:

Context.Provider를 사용하여 컨텍스트 값을 하위 컴포넌트에 전달해야 했습니다.

const MyContext = React.createContext(defaultValue);

function App() {
  return (
    <MyContext.Provider value="Updated Value">
      <ChildComponent />
    </MyContext.Provider>
  );
}

변경 후:

React 19에서는 태그 자체를 프로바이더로 사용할 수 있습니다.

const MyContext = React.createContext(defaultValue);

function App() {
  return (
    <MyContext value="Updated Value">
      <ChildComponent />
    </MyContext>
  );
}

이 변경은 Context API를 사용할 때 코드를 더 간결하게 만들어 줍니다.

4. ref 콜백에서 클린업 함수 지원

변경 전:

ref 콜백은 컴포넌트가 마운트될 때 DOM 요소를 참조하기 위해 사용되었으나, 언마운트 시 클린업 로직을 직접 처리해야 했습니다.

function App() {
  const ref = useCallback(node => {
    if (node !== null) {
      // 초기화 로직
    }
  }, []);
}

변경 후:

React 19에서는 ref 콜백이 클린업 함수를 반환할 수 있게 되어, 컴포넌트가 DOM에서 제거될 때 자동으로 클린업 로직을 실행할 수 있습니다.

function App() {
  const ref = useCallback(node => {
    if (node !== null) {
      // 초기화 로직
      return () => {
        // 클린업 로직
      };
    }
  }, []);
}

이러한 개선으로 리소스를 더 효율적으로 관리할 수 있게 되었습니다. 이 변경들은 React 19의 사용성을 향상시키고 개발자가 더 효율적으로 코드를 작성할 수 있도록 도와줍니다.

profile
프론트엔드 개발자를 준비하고 있습니다.

0개의 댓글