학습 자료

use() 와 그 밖에 달라진 것들


Article

마지막 강의다. React 19에서 달라진 나머지를 모아 본다.

가장 이상한 것부터 시작한다. 훅의 규칙을 지키지 않아도 되는 것이 생겼다.

use() — 훅이 아니다

16강에서 훅은 최상위에서만 부르라고 했다. 조건문 안에서 부르면 순서가 깨진다.

그런데 use()는 된다.

import { use } from "react";

function Panel({ showTheme, themeContext }) {
  if (showTheme) {
    const theme = use(themeContext);
    return <div className={theme}></div>;
  }
  return <div></div>;
}

조건문 안에 있다. 반복문 안에서도 부를 수 있다. 어떻게 가능할까.

16강에서 규칙의 근거를 "React는 훅을 순서로 기억한다"고 했다. 기억하지 않는 것에는 그 규칙이 적용되지 않는다. 규칙을 외웠다면 헷갈렸겠지만, 이유를 알면 자연스럽다.

훅과 use()

useState · useEffect

값을 기억한다

  • 컴포넌트마다 자리를 만든다
  • 순서로 그 자리를 찾는다
  • 최상위에서만 부른다
  • 조건문 · 반복문 금지

use()

값을 읽기만 한다

  • 자리를 만들지 않는다
  • 기억할 게 없다
  • 어디서든 부른다
  • 조건문 · 반복문 가능

use() 로 무엇을 읽나

두 가지다. ContextPromise.

Context를 읽는 건 useContext와 같다.

const theme = useContext(ThemeContext);
const theme = use(ThemeContext);

차이는 조건부로 부를 수 있다는 것뿐이다. 18강에서 만든 커스텀 훅 패턴은 그대로 쓴다.

Promise를 읽는 쪽이 새롭다.

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(c => <p key={c.id}>{c.text}</p>);
}

function Page({ commentsPromise }) {
  return (
    <Suspense fallback={<Spinner />}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  );
}

use(promise)Promise가 끝날 때까지 컴포넌트를 멈춘다. 그동안 가장 가까운 <Suspense>fallback이 대신 그려진다.

use(promise) 가 하는 일
  1. 컴포넌트 실행use(promise) 를 만난다
  2. 멈춘다Suspense 가 fallback 을 그린다
  3. Promise 완료값이 준비됐다
  4. 다시 실행이번엔 값을 반환한다

15강에서 데이터를 받아오던 코드와 비교해보자.

useEffect 와 use()

useEffect

직접 관리한다

  • isLoading state 를 만든다
  • error state 를 만든다
  • 경쟁 상태를 ignore 로 막는다
  • 화면이 그려진 뒤에 요청한다

use() + Suspense

React 가 관리한다

  • 로딩은 Suspense
  • 에러는 ErrorBoundary
  • 경쟁 상태가 없다
  • 요청은 부모가 미리 시작한다

마지막 항목이 중요하다. useEffect는 화면을 그린 뒤에야 요청을 보냈다. use()는 부모가 이미 시작해둔 Promise를 받아 기다린다. 요청이 더 일찍 나간다.

use()를 순수 React 앱에서 제대로 쓰려면 Promise를 캐싱해주는 장치가 필요하다. 그래서 실제로는 Next.js 같은 프레임워크나 React Query 같은 라이브러리와 함께 쓴다. 지금은 이런 게 있다는 것만 알아둔다.

ref 콜백이 정리 함수를 반환한다

17강에서 ref로 DOM을 다뤘다. ref에 함수를 넘길 수도 있는데, React 19부터 그 함수가 정리 함수를 반환할 수 있다.

<div
  ref={node => {
    const observer = new ResizeObserver(() => {});
    observer.observe(node);

    return () => observer.disconnect();
  }}
/>

15강의 useEffect 정리 함수와 똑같은 모양이다. 요소가 사라질 때 React가 그 함수를 부른다.

여기서 기존 코드가 깨지는 함정이 하나 생겼다.

<input ref={node => (inputRef.current = node)} />

자바스크립트 편 11강에서 배운 대로, 화살표 함수에 중괄호가 없으면 암묵적으로 반환한다. 이 코드는 node를 반환한다. React 19는 그걸 정리 함수로 착각하고 에러를 낸다.

콘솔
  • Unexpected return value from a callback ref

중괄호를 쳐서 아무것도 반환하지 않게 한다.

<input ref={node => { inputRef.current = node; }} />

문서 메타데이터를 컴포넌트에서 쓴다

<title>이나 <meta>를 컴포넌트 안에서 그냥 렌더링하면, React가 알아서 <head>로 옮겨준다.

function Post({ post }) {
  return (
    <article>
      <title>{post.title}</title>
      <meta name="description" content={post.summary} />

      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  );
}

예전에는 react-helmet 같은 라이브러리가 하던 일이다. 이제 React가 직접 한다.

<link><script>도 같은 대우를 받는다.

정리해서 사라진 것들

17강과 18강에서 짚은 두 가지를 여기 모은다.

React 18까지React 19부터
forwardRef(Component)ref 를 props 로 받는다감쌀 이유가 없어졌다
<Context.Provider><Context>군더더기였다
useFormState (react-dom)useActionState (react)이름과 위치를 바로잡았다
propTypes · defaultProps제거됨 (함수 컴포넌트)TypeScript 와 기본 매개변수로

마지막 줄이 주의할 점이다. 함수 컴포넌트의 defaultProps동작하지 않는다. 5강에서 배운 기본 매개변수를 쓴다.

function Greeting({ name = "손님" }) {
  return <p>{name}님</p>;
}

이름만 알아둘 것들

깊이 다루지 않지만 들어봤을 것들을 정리한다.

React 19 언저리의 이름들
  • React 19
    • Server Components서버에서 렌더링되는 컴포넌트
      • 프레임워크가 필요하다 (Next.js 등)
    • Server Actions"use server" — 서버에서 도는 Action
      • 19강에서 이름만 봤다
    • React CompileruseMemo · useCallback 을 자동으로
      • React 19 에 포함된 게 아니다. 별도 도구다

React Compiler는 React 19의 일부가 아니다. 별개의 도구이고, 아직 도입 초기다. 이걸 쓰면 useMemouseCallback을 손으로 붙일 일이 줄어든다.

13강에서 "느려진 뒤에 재보고 고친다"고 했는데, 컴파일러가 성숙하면 그 고민마저 줄어들 것이다.

21강을 마치며

여기까지가 React다. 1강에서 "화면을 직접 고치지 않는다"로 시작해서, 마지막 강의에서 React가 우리 대신 해주는 일이 더 늘어난 것으로 끝났다.

돌아보면 같은 이야기가 반복해서 나왔다.

이 시리즈를 관통한 것들
  1. 01

    UI = f(state)

    1강에서 시작해 끝까지 이어졌다. 화면은 결과일 뿐이고, 우리는 상태만 바꾼다. 20강의 낙관적 업데이트조차 "상태를 잠깐 덧씌우는" 일이었다.

  2. 02

    React 는 주소로 비교한다

    9강 · 12강의 불변성, 13강의 리렌더링, 15강의 의존성 배열, 18강의 Context 가 전부 이 하나에서 나온다.

  3. 03

    상태는 위치로 기억된다

    7강의 조건부 렌더링, 11강의 key, 13강 · 14강의 재조정, 16강의 훅 순서가 같은 규칙을 말한다.

  4. 04

    데이터는 아래로, 이벤트는 위로

    5강의 단방향 흐름이 6강의 합성, 18강의 상태 끌어올리기로 이어졌다.

React 19가 준 것들도 결국 하나의 방향이다. 반복되는 일을 React가 가져간다. isPending을 직접 만들지 않고, 되돌리는 코드를 쓰지 않고, forwardRef로 감싸지 않는다.

문법을 다 알았다고 좋은 컴포넌트를 짜게 되지는 않는다. 다음은 직접 만들어보는 일이다. 여기서 배운 것을 손으로 쳐보고, 왜 다시 그려지는지 Profiler로 들여다보고, 에러 메시지를 끝까지 읽으면 된다.

그리고 React를 배우는 가장 빠른 길은 자바스크립트를 잘 아는 것이다. map, 구조분해, 스프레드, 클로저, 주소 비교, 화살표 함수의 암묵적 반환 — 이 시리즈에서 React 문법이라고 배운 것은 거의 없었다. 대부분 자바스크립트였다.

고생했다.

정리하면

  • use()는 훅이 아니다. 값을 기억하지 않으므로 조건문·반복문 안에서 불러도 된다.
  • use()ContextPromise를 읽는다. Promise를 읽으면 Suspense가 로딩을 맡는다.
  • 렌더 중에 Promise를 만들지 않는다. 부모나 바깥에서 만들어 넘긴다.
  • ref 콜백이 정리 함수를 반환할 수 있다. 화살표 함수의 암묵적 반환을 조심한다.
  • <title>, <meta>를 컴포넌트 안에서 렌더링하면 <head>로 옮겨진다.
  • forwardRef, <Context.Provider>, 함수 컴포넌트의 defaultProps가 필요 없어졌다.
  • React Compiler는 React 19의 일부가 아니다. 별도 도구다.
use() 와 그 밖에 달라진 것들 — 디코드랩(DCODELAB)