header-img
Info :
728x90

 

 

React 19 Beta – React

The library for web and native user interfaces

react.dev

 

์ผ๋ถ€ ํ•ด์„ ๋ฐœ์ทŒ

 

React 19์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ

Action

๋ฆฌ์•กํŠธ ์•ฑ์˜ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ ๋‹ค์Œ ์‘๋‹ต์œผ๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด ์–‘์‹์„ ์ œ์ถœํ•˜๋ฉด API ์š”์ฒญ์„ ํ•œ ๋‹ค์Œ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ณผ๊ฑฐ์—๋Š” ๋ณด๋ฅ˜ ์ƒํƒœ, ์˜ค๋ฅ˜, ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ๋ฐ ์ˆœ์ฐจ์  ์š”์ฒญ์„ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด usestate์—์„œ ๋ณด๋ฅ˜ ๋ฐ ์˜ค๋ฅ˜ ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// 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 ์—์„œ๋Š” ๋ณด๋ฅ˜ ์ค‘์ธ ์ƒํƒœ, ์˜ค๋ฅ˜, ์–‘์‹ ๋ฐ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ „ํ™˜์— Async functions์„ ์‚ฌ์šฉํ•˜๋Š” ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด, 'useTransition'์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด๋ฅ˜ ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

 

๋น„๋™๊ธฐ ์ „ํ™˜์€ ์ฆ‰์‹œ isPending ์ƒํƒœ๋ฅผ True๋กœ ์„ค์ •ํ•˜๊ณ , ๋น„๋™๊ธฐ ์š”์ฒญ์„ ํ•˜๊ณ , isPending ์ „ํ™˜ ํ›„์—๋Š” false๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๋™์•ˆ ํ˜„์žฌ UI์˜ ๋ฐ˜์‘์„ฑ๊ณผ ๋Œ€ํ™”ํ˜•์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ด€๋ก€์ ์œผ๋กœ ๋น„๋™๊ธฐ ์ „ํ™˜์„ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ 'Action'์ด๋ผ๊ณ  ํ•œ๋‹ค.

Action์€ ์ œ์ถœ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ.
- Pending State: ์ž‘์—…์€ ์š”์ฒญ ์‹œ์ž‘ ์‹œ ์‹œ์ž‘๋˜๊ณ  ์ตœ์ข… ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ปค๋ฐ‹๋˜๋ฉด ์ž๋™์œผ๋กœ ์žฌ์„ค์ •๋˜๋Š” ๋ณด๋ฅ˜ ์ƒํƒœ๋ฅผ ์ œ๊ณต
- Optimistic Updates: ์ž‘์—…์€ ์ƒˆ๋กœ์šด useOptimistic ํ›…์„ ์ง€์›ํ•˜๋ฏ€๋กœ ์š”์ฒญ์ด ์ œ์ถœ๋˜๋Š” ๋™์•ˆ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ฆ‰๊ฐ์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Œ
- Error handling: ์ž‘์—…์€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์š”์ฒญ์ด ์‹คํŒจํ•  ๋•Œ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„๋ฅผ ํ‘œ์‹œํ•˜๊ณ , ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ์›๋ž˜ ๊ฐ’์œผ๋กœ ์ž๋™์œผ๋กœ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์Œ.
- Forms: <form> ์š”์†Œ๋Š” ์ด์ œ Action ๋ฐ formAction ์†Œํ’ˆ์— ํ•จ์ˆ˜ ์ „๋‹ฌ์„ ์ง€์›ํ•จ. props์— ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋ฉด action ๊ธฐ๋ณธ์ ์œผ๋กœ Actions๊ฐ€ ์‚ฌ์šฉ๋˜๋ฉฐ ์ œ์ถœ ํ›„ ์ž๋™์œผ๋กœ ์–‘์‹์ด ์žฌ์„ค์ •๋จ.

 

Actions๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ React 19์—์„œ๋Š” useOptimistic ๊ณผ Actions์˜ ์ผ๋ฐ˜์ ์ธ ์‚ฌ๋ก€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ƒˆ๋กœ์šด ํ›…์ธ React.useActionState๋ฅผ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค. React-dom์—์„œ๋Š” ์–‘์‹์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” 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 (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

 

 

New Hook: useActionState

์ผ๋ฐ˜์ ์ธ ์‚ฌ๋ก€๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก 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
});

 

useActionState Function("Action")๋ฅผ Pendingํ•˜๊ณ  ํ˜ธ์ถœํ•  Action์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์•ก์…˜์ด ๊ตฌ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ž˜ํ•‘๋œ ์•ก์…˜์ด ํ˜ธ์ถœ๋˜๋ฉด useActionState๋Š” ์•ก์…˜์˜ ๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ดํ„ฐ๋กœ, ๋ณด๋ฅ˜ ์ค‘์ธ ์•ก์…˜์˜ ์ƒํƒœ๋ฅผ Pending์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

์ด์ „์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฆด๋ฆฌ์ฆˆ์—์„œ React.useActionState๋ฅผ ReactDOM.useFormState๋ผ๊ณ  ๋ถˆ๋ €์ง€๋งŒ
FormState๋ผ๋Š” ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๊ณ , useFormState๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ๋ณ€๊ฒฝํ–ˆ๋‹ค.
 

Add `React.useActionState` by rickhanlonii · Pull Request #28491 · facebook/react

Overview Depends on #28514 This PR adds a new React hook called useActionState to replace and improve the ReactDOM useFormState hook. Motivation This hook intends to fix some of the confusion and l...

github.com

 

React Dom: <form> Actions

Action์€ React-DOM์„ ์œ„ํ•œ React 19์˜ ์ƒˆ๋กœ์šด <form> ๊ธฐ๋Šฅ๊ณผ๋„ ํ†ตํ•ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. Action๊ณผ ํ•จ๊ป˜ ์ž๋™์œผ๋กœ ์–‘์‹์„ ์ œ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก <form>, <input>, <button> ์š”์†Œ์˜ Action๊ณผ formAction Props๋กœ ํ†ต๊ณผ ๊ธฐ๋Šฅ ์ง€์›์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

<form action={actionFunction}>

 

<form> Action์ด ์„ฑ๊ณตํ•˜๋ฉด React๋Š” ์ œ์–ด๋˜์ง€ ์•Š๋Š” ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ์ž๋™์œผ๋กœ ์–‘์‹์„ ์žฌ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. <form>์„ ์ˆ˜๋™์œผ๋กœ ์žฌ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ์ƒˆ๋กœ์šด requestFormResetReactDOM API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

React DOM: New Hook: useFormStatus

์„ค๊ณ„ ์‹œ์Šคํ…œ์—์„œ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  <form>์— ๋Œ€ํ•œ ์ •๋ณด์— ์—‘์„ธ์Šคํ•ด์•ผ ํ•˜๋Š” ์„ค๊ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ Context๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒˆ ํ›…์ธ useFormStatus๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. 

import {useFormStatus} from 'react-dom';

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

 

useFormStatus๋Š” ์–‘์‹์ด ์ปจํ…์ŠคํŠธ ๊ณต๊ธ‰์ž์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ถ€๋ชจ <form>์˜ ์ƒํƒœ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค.

 

New Hook: useOptimistic

๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ๋˜ ๋‹ค๋ฅธ ์ผ๋ฐ˜์ ์ธ UI ํŒจํ„ด์€ ๋น„๋™๊ธฐ ์š”์ฒญ์ด ์ง„ํ–‰๋˜๋Š” ๋™์•ˆ ์ตœ์ข… ์ƒํƒœ๋ฅผ ๋‚™๊ด€์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ. React 19 ์—์„œ๋Š” ์ด๋ฅผ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด useOptimistic ๋ผ๋Š” ์ƒˆ๋กœ์šด Hook์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

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

 

updateName ์š”์ฒญ์ด ์ง„ํ–‰๋˜๋Š” ๋™์•ˆ useOptimistic Hook์„ ์‚ฌ์šฉํ•˜๋ฉด OptimisticName์ด ์ฆ‰์‹œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ๋˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด React๋Š” ์ž๋™์œผ๋กœ CurrentName ๊ฐ’์œผ๋กœ ๋‹ค์‹œ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.

 

New API: Use

Render๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ์šด API์ธ use.

 

์˜ˆ๋ฅผ ๋“ค์–ด, use์™€ ์•ฝ์†์„ ์ฝ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์•ฝ์†์ด ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ๋ฐ˜์‘์ด ์ผ์‹œ ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.

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๋Š” ๋ Œ๋”๋กœ ์ž‘์„ฑ๋œ ์•ฝ์†์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•  ๋ Œ๋”์—์„œ ๋งŒ๋“  ์•ฝ์†์„ ์ „๋‹ฌํ•˜๋ ค๊ณ  ํ•˜๋ฉด React๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฒฝ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

์ˆ˜์ •ํ•˜๋ ค๋ฉด promises์— ๋Œ€ํ•œ ์บ์‹ฑ์„ ์ง€์›ํ•˜๋Š” ์„œ์ŠคํŽœ์Šค ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ promise๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ–ฅํ›„์—๋Š” ๋ Œ๋”๋ง์—์„œ promises๋ฅผ ๋” ์‰ฝ๊ฒŒ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ „๋‹ฌํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

 

๋˜ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์กฐ๊ธฐ ๋ฐ˜ํ™˜ ํ›„์™€ ๊ฐ™์€ ์กฐ๊ฑด๋ถ€ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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 ๋Š” Hook๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ Œ๋”์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Hook๊ณผ ๋‹ฌ๋ฆฌ Use๋Š” ์กฐ๊ฑด๋ถ€๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ์šฐ๋ฆฌ๋Š” ๋ Œ๋”์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์†Œ๋น„ํ•˜๋Š” ๋” ๋งŽ์€ ๋ฐฉ๋ฒ•์„ ์ง€์›ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

 

React Server Component

(์ƒ๋žต)

 

React 19์˜ ๊ฐœ์„  ์‚ฌํ•ญ

Ref as a Prop

Ref๋ฅผ Function Component์˜ prop ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

 

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๋” ์ด์ƒ forwardRef๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์ƒˆ๋กœ์šด ์ฐธ์กฐ prop์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝ”๋“œ ๋ชจ๋“œ๋ฅผ ๊ฒŒ์‹œํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ํ–ฅํ›„ ๋ฒ„์ „์—์„œ๋Š” forwardRef๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ œ๊ฑฐํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

ํด๋ž˜์Šค์— ์ „๋‹ฌ๋œ refs๋Š” ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๋ฏ€๋กœ prop์œผ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

<Context> as a provider

React 19 ์—์„œ๋Š” <Context.Provider> ๋Œ€์‹  <Context>๋ฅผ ๊ณต๊ธ‰์ž๋กœ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const ThemeContext = createContext('');

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

 

์ƒˆ Context ์ œ๊ณต์ž๋Š” <Context>๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ธฐ์กด ์ œ๊ณต์ž๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ ๋ชจ๋“œ๋ฅผ ๊ฒŒ์‹œํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ํ–ฅํ›„ ๋ฒ„์ „์—์„œ๋Š” <Context.Provider>๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

Cleanup functions for refs

์ด์ œ ref callback ์—์„œ ์ •๋ฆฌ ๊ธฐ๋Šฅ์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<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 Ref, class, Component Ref, useImperativeHandle์— ๋Œ€ํ•ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์ด์ „์— React๋Š” ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งˆ์šดํŠธ ํ•ด์ œํ•  ๋•Œ null๋กœ Ref ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. Ref๊ฐ€ CleanUp ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด React๋Š” ์ด์ œ ์ด ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.
ํ–ฅํ›„ ๋ฒ„์ „์—์„œ๋Š” ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งˆ์šดํŠธ ํ•ด์ œํ•  ๋•Œ null๋กœ ์ฐธ์กฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๊ธˆ์ง€ํ•ฉ๋‹ˆ๋‹ค.

 

useDefferedValue initial value

useDefferedValue์— Initial Value ์˜ต์…˜์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

(useDefferedValue ๋Š” ๊ฐ’์˜ ์—…๋ฐ์ดํŠธ ์šฐ์„  ์ˆœ์œ„๋ฅผ ์ง€์ •ํ•˜๋Š” Hook)

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๊ฐ€ ์ œ๊ณต๋˜๋ฉด useDefferredValue ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ดˆ๊ธฐ ๋ Œ๋”์— ๋Œ€ํ•œ ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ  deferrefValue re-render๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ re-render์„ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

 

useDeferredValue – React

The library for web and native user interfaces

react.dev

 

Support for Document Metadata

React 19 ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ๋ฌธ์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํƒœ๊ทธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

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

 

๋ฆฌ์•กํŠธ๊ฐ€ ์ด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด <title>, <link>, <meta> ํƒœ๊ทธ๊ฐ€ ํ‘œ์‹œ๋˜๊ณ  ์ž๋™์œผ๋กœ ๋ฌธ์„œ์˜ <head> ์„น์…˜์œผ๋กœ ํ˜ธ์ด์ŠคํŠธ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํƒœ๊ทธ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•จ์œผ๋กœ์จ ํด๋ผ๋ฆฌ์–ธํŠธ ์ „์šฉ ์•ฑ, ์ŠคํŠธ๋ฆฌ๋ฐ SSR ๋ฐ ์„œ๋ฒ„ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Support for StyleSheets

์Šคํƒ€์ผ์‹œํŠธ๋Š” ์™ธ๋ถ€๋งํฌ(<link rel='stylesheet' href="....">)์™€ ์ธ๋ผ์ธ(<style>...</style>) ๋ชจ๋‘ ์Šคํƒ€์ผ ์šฐ์„  ์ˆœ์œ„ ๊ทœ์น™์œผ๋กœ ์ธํ•ด DOM์—์„œ ์‹ ์ค‘ํ•œ ์œ„์น˜ ์ง€์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ ํ•ฉ์„ฑ์„ ํ—ˆ์šฉํ•˜๋Š” ์Šคํƒ€์ผ์‹œํŠธ ๊ธฐ๋Šฅ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๋Š” ์ข…์ข… ์ž์‹ ์—๊ฒŒ ์˜์กดํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ๋ฉ€๋ฆฌ ๋–จ์–ด์ง„ ๋ชจ๋“  ์Šคํƒ€์ผ์„ ๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ์ด ๋ณต์žก์„ฑ์„ ์บก์Šํ™”ํ•˜๋Š” ์Šคํƒ€์ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

React 19์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ณต์žก์„ฑ์„ ํ•ด๊ฒฐํ•˜๊ณ  ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ์ง€์›ํ•˜๋Š” ๋‚ด์žฅ๋œ ํด๋ผ์ด์–ธํŠธ์˜ ๋™์‹œ ๋ Œ๋”๋ง๊ณผ ์„œ๋ฒ„์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ Œ๋”๋ง์— ๋”์šฑ ๊นŠ์ด ์žˆ๋Š” ํ†ตํ•ฉ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. React 19์— ์Šคํƒ€์ผ์‹œํŠธ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์•Œ๋ ค์ฃผ๋ฉด 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>์— ์‚ฝ์ž…๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

ํด๋ผ์ด์–ธํŠธ ์ธก ๋ Œ๋”๋ง ์ค‘์— React๋Š” ๋ Œ๋”๋ง์„ ์ปค๋ฐ‹ํ•˜๊ธฐ ์ „์— ์ƒˆ๋กœ ๋ Œ๋”๋ง ๋œ ์Šคํƒ€์ผ์‹œํŠธ๊ฐ€ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋‚ด์˜ ์—ฌ๋Ÿฌ ์žฅ์†Œ์—์„œ ์ด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒฝ์šฐ React๋Š” ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ๋ฌธ์„œ์— ํ•œ ๋ฒˆ๋งŒ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

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

 

Support for Async scripts

HTML ์ผ๋ฐ˜ ์Šคํฌ๋ฆฝํŠธ(<script src="...">)์™€ ์ง€์—ฐ ์Šคํฌ๋ฆฝํŠธ(<script depar="" src="...">)๋Š” ๋ฌธ์„œ ์ˆœ์„œ๋กœ ๋กœ๋“œ๋˜๋ฏ€๋กœ ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ์˜ ๊นŠ์€ ๊ณณ์— ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋น„๋™๊ธฐ ์Šคํฌ๋ฆฝํŠธ(<script async="" src="...">)๋Š” ์ž„์˜์˜ ์ˆœ์„œ๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

 

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

 

 

Support for preloading resources

์ดˆ๊ธฐ ๋ฌธ์„œ ๋กœ๋“œ ๋ฐ ํด๋ผ์ด์–ธํŠธ ์ธก ์—…๋ฐ์ดํŠธ ์ค‘์— ๋ธŒ๋ผ์šฐ์ €์— ๊ฐ€๋Šฅํ•œ ํ•œ ๋นจ๋ฆฌ ๋กœ๋“œํ•ด์•ผ ํ•  ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•ด ์•Œ๋ ค์ฃผ๋ฉด ํŽ˜์ด์ง€ ์„ฑ๋Šฅ์— ๊ทน์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

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>

 

 

 

Support for Custom Elements

React 19๋Š” Custom Elements์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๊ณ  Custom Elements Everywhere์— ๋Œ€ํ•œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค.

 

์ด์ „ ๋ฒ„์ „์—์„œ๋Š” React๊ฐ€ ์ธ์‹๋˜์ง€ ์•Š๋Š” Props๋ฅผ ์†์„ฑ์ด ์•„๋‹Œ ์†์„ฑ์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— React์—์„œ Custom Elements๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค. React 19์—์„œ๋Š” ํด๋ผ์ด์–ธํŠธ ๋ฐ SSR ์ค‘์— ์ž‘๋™ํ•˜๋Š” ์†์„ฑ์— ๋Œ€ํ•œ ์ง€์›์„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ „๋žต์œผ๋กœ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

Server Side Rendering: ์‚ฌ์šฉ์ž ์ง€์ • ์š”์†Œ์— ์ „๋‹ฌ๋œ props๋Š” ํ•ด๋‹น ์œ ํ˜•์ด ๋ฌธ์ž์—ด, ์ˆซ์ž ๋˜๋Š” ๊ฐ’์ด true์™€ ๊ฐ™์€ ๊ธฐ๋ณธ ๊ฐ’์ด๋ฉด ์†์„ฑ์œผ๋กœ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๊ฐœ์ฒด, ๊ธฐํ˜ธ, ํ•จ์ˆ˜ ๋˜๋Š” ๊ฐ’ false์™€ ๊ฐ™์€ primitive๊ฐ€ ์•„๋‹Œ ์œ ํ˜•์˜ props๋Š” ์ƒ๋žต๋ฉ๋‹ˆ๋‹ค.

Client Side Rendering: Custom Element ์ธ์Šคํ„ด์Šค์˜ ์†์„ฑ๊ณผ ์ผ์น˜ํ•˜๋Š” props๋Š” properties๋กœ ํ• ๋‹น๋˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด attributes๋กœ ํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

 

 

 

 

 

728x90
๋”๋ณด๊ธฐ
FRONTEND/React