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는 훅을 순서로 기억한다"고 했다. 기억하지 않는 것에는 그 규칙이 적용되지 않는다. 규칙을 외웠다면 헷갈렸겠지만, 이유를 알면 자연스럽다.
useState · useEffect
값을 기억한다
- 컴포넌트마다 자리를 만든다
- 순서로 그 자리를 찾는다
- 최상위에서만 부른다
- 조건문 · 반복문 금지
use()
값을 읽기만 한다
- 자리를 만들지 않는다
- 기억할 게 없다
- 어디서든 부른다
- 조건문 · 반복문 가능
use() 로 무엇을 읽나
두 가지다. Context와 Promise.
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) 를 만난다
- 멈춘다Suspense 가 fallback 을 그린다
- Promise 완료값이 준비됐다
- 다시 실행이번엔 값을 반환한다
15강에서 데이터를 받아오던 코드와 비교해보자.
useEffect
직접 관리한다
isLoadingstate 를 만든다errorstate 를 만든다- 경쟁 상태를
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
- Server Components
- 프레임워크가 필요하다 (Next.js 등)
- Server Actions
- 19강에서 이름만 봤다
- React Compiler
- React 19 에 포함된 게 아니다. 별도 도구다
- Server Components
React Compiler는 React 19의 일부가 아니다. 별개의 도구이고, 아직 도입 초기다. 이걸 쓰면 useMemo와 useCallback을 손으로 붙일 일이 줄어든다.
13강에서 "느려진 뒤에 재보고 고친다"고 했는데, 컴파일러가 성숙하면 그 고민마저 줄어들 것이다.
21강을 마치며
여기까지가 React다. 1강에서 "화면을 직접 고치지 않는다"로 시작해서, 마지막 강의에서 React가 우리 대신 해주는 일이 더 늘어난 것으로 끝났다.
돌아보면 같은 이야기가 반복해서 나왔다.
- 01
UI = f(state)
1강에서 시작해 끝까지 이어졌다. 화면은 결과일 뿐이고, 우리는 상태만 바꾼다. 20강의 낙관적 업데이트조차 "상태를 잠깐 덧씌우는" 일이었다.
- 02
React 는 주소로 비교한다
9강 · 12강의 불변성, 13강의 리렌더링, 15강의 의존성 배열, 18강의 Context 가 전부 이 하나에서 나온다.
- 03
상태는 위치로 기억된다
7강의 조건부 렌더링, 11강의 key, 13강 · 14강의 재조정, 16강의 훅 순서가 같은 규칙을 말한다.
- 04
데이터는 아래로, 이벤트는 위로
5강의 단방향 흐름이 6강의 합성, 18강의 상태 끌어올리기로 이어졌다.
React 19가 준 것들도 결국 하나의 방향이다. 반복되는 일을 React가 가져간다. isPending을 직접 만들지 않고, 되돌리는 코드를 쓰지 않고, forwardRef로 감싸지 않는다.
문법을 다 알았다고 좋은 컴포넌트를 짜게 되지는 않는다. 다음은 직접 만들어보는 일이다. 여기서 배운 것을 손으로 쳐보고, 왜 다시 그려지는지 Profiler로 들여다보고, 에러 메시지를 끝까지 읽으면 된다.
그리고 React를 배우는 가장 빠른 길은 자바스크립트를 잘 아는 것이다. map, 구조분해, 스프레드, 클로저, 주소 비교, 화살표 함수의 암묵적 반환 — 이 시리즈에서 React 문법이라고 배운 것은 거의 없었다. 대부분 자바스크립트였다.
고생했다.
정리하면
use()는 훅이 아니다. 값을 기억하지 않으므로 조건문·반복문 안에서 불러도 된다.use()로 Context와 Promise를 읽는다. Promise를 읽으면Suspense가 로딩을 맡는다.- 렌더 중에 Promise를 만들지 않는다. 부모나 바깥에서 만들어 넘긴다.
ref콜백이 정리 함수를 반환할 수 있다. 화살표 함수의 암묵적 반환을 조심한다.<title>,<meta>를 컴포넌트 안에서 렌더링하면<head>로 옮겨진다.forwardRef,<Context.Provider>, 함수 컴포넌트의defaultProps가 필요 없어졌다.- React Compiler는 React 19의 일부가 아니다. 별도 도구다.