이 글은 원저자의 허락을 맡아 <New client-side hooks coming to React 19>을 한국어로 번역한 글입니다.
일반적인 생각과는 달리, React 코어 팀은 React 서버 컴포넌트와 Next.js에만 집중하는 건 아닙니다. 새로운 클라이언트 사이드 훅은 다음 메이저 버전인 React 19에서 제공될 예정입니다. 이 훅은 데이터 페칭과 폼이라는 React의 두 가지 주요 문제점에 초점을 맞추고 있습니다. 새롭게 등장할 훅은 SPA 개발자를 포함한 모든 React 개발자의 생산성을 향상시킬 것입니다.
각설하고 새로운 훅에 대해 알아보겠습니다.
주의: 이 훅들은 React 카나리 및 실험 버전에서만 사용 가능합니다. React 19에서 추가될 예정이지만 최종 릴리즈 전에 API가 변경될 수 있습니다.
이 새로운 훅은 클라이언트에서 "중단(suspending)"하는 공식 API입니다. 프로미스를 전달하면 해당 프로미스가 해결될 때까지 React는 중단됩니다. React use
문서에서 가져온 기본 구문은 다음과 같습니다.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
// ...
}
좋은 소식은 이 훅을 데이터 페칭에 사용할 수 있다는 것입니다. 다음은 마운트 및 버튼 클릭 시 데이터를 가져오는 구체적인 예제입니다. 이 코드는 useEffect
를 전혀 사용하지 않습니다.
<Suspense>
문서에 있던 이 경고를 기억하시나요?
강제성이 있는 프레임워크를 사용하지 않고는 아직 suspense을 지원하는 데이터 페칭이 지원되지 않습니다.
그러나 React 19에서는 더 이상 그렇지 않습니다.
새로운 use
훅에는 숨겨진 기능이 있습니다. 다른 React 훅과 달리 use
는 if
와 같은 루프 및 조건문 내에서 호출될 수 있다는 점입니다.
우리가 이제 클라이언트 사이드에서 데이터를 가져오기 위해 TanStack Query와 같은 서드파티 라이브러리를 더 이상 필요로 하지 않다는 것일까요? 글쎄요, Tanstack Query는 Promise를 해결하는 것 이상의 일을 수행하고 있기 때문에 아직은 좀 더 지켜봐야 할 것 같습니다.
어쨌든 이는 올바른 방향으로 나아가는 큰 발걸음이며, REST 또는 GraphQL API를 기반으로 SPA를 더 쉽게 구축할 수 있을 것입니다. 저는 이 새로운 훅이 아주 반갑습니다!
React 문서에서 use(Promise)
훅에 대해 더 자세히 알아보세요.
동일한 use
훅을 사용하여 React 컨텍스트를 읽을 수 있습니다. if
와 같은 조건문과 루프 내에서 호출할 수 있다는 점을 제외하면 useContext
와 완전히 동일합니다.
import { use } from 'react';
function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}
루프나 조건문에서 컨텍스트를 읽는 유일한 방법은 컴포넌트를 두 개로 나누는 것이었기 때문에, 특정 경우에서는 컴포넌트 계층 구조가 더 단순해집니다.
컨텍스트가 변경되었더라도 조건에 따라 컴포넌트의 리렌더링을 생략할 수 있으므로 성능 측면에서도 큰 발전입니다.
React 문서에서 use(Context)
훅에 대해 더 자세히 알아보세요.
이 기능을 사용하면 <form>
의 action
프로퍼티에 함수를 전달할 수 있습니다. React는 폼이 제출될 때 이 함수를 호출합니다.
<form action={handleSubmit} />
React 18에서 <form action>
속성을 추가하면 다음과 같은 경고가 발생합니다.
경고:
<form>
태그의action
프로퍼티에 대한 잘못된 값입니다. 요소에서 값을 제거하거나, 해당 요소에 문자열 또는 숫자 값을 전달하여 DOM에 유지하세요.
React 19에서는 해당 경고가 발생하지 않으며, 이제 다음과 같이 폼을 작성할 수 있습니다.
addToCart
함수는 서버 액션(Server Action)이 아닙니다. 클라이언트 사이드에서 호출되며 비동기 함수가 될 수 있습니다.
이는 React에서 검색 폼과 같은 AJAX 폼 처리를 크게 간소화할 것입니다. 그러나 이것만으로는 React Hook Form과 같은 서드파티 라이브러리를 완전히 대체할 순 없을 것입니다. React Hook Form은 폼 제출뿐만 아니라 유효성 검사, 부수 효과 등 다양한 작업을 처리하기 때문입니다.
팁: 위의 예제에서 몇 가지 사용성 문제를 발견할 수 있습니다.(제출 중에 제출 버튼이 비활성화되지 않음, 확인 메시지 미노출, 장바구니의 업데이트 지연). 다행히도 이러한 경우에 도움이 될 만한 훅이 더 많이 나오고 있습니다. 계속 읽어보세요!
React 문서에서 <form action>
프로퍼티에 대해 더 자세히 읽을 수 있습니다.
이 새로운 훅은 위에서 설명한 비동기 폼 액션 기능을 지원하는 데 목적을 두고 있습니다. useFormState
를 호출하여, 폼이 마지막으로 제출됐을 때 액션의 반환 값에 접근할 수 있습니다.
import { useFormState } from 'react-dom';
import { action } from './action';
function MyComponent() {
const [state, formAction] = useFormState(action, null);
// ...
return <form action={formAction}>{/* ... */}</form>;
}
예를 들어, 폼 액션에서 반환된 확인 또는 오류 메시지를 표시할 수 있습니다.
주의: useFormState
는 react
가 아니라 react-dom
에서 가져와야 합니다.
React 문서에서 useFormstate
훅에 대해 더 자세히 알아볼 수 있습니다.
useFormStatus
는 상위에 있는 <form>
이 현재 제출 중인지 또는 성공적으로 제출되었는지를 알려줍니다. 폼의 하위에서 호출할 수 있으며 다음과 같은 속성을 반환하는 객체를 제공합니다.
const { pending, data, method, action } = useFormStatus();
data
속성을 사용하여 사용자가 제출하는 데이터를 표시할 수 있습니다. 또한 다음 예제처럼 폼이 제출 중일 때 버튼을 비활성화하는 등의 대기 상태를 나타낼 수 있습니다.
주의: useFormState
은 react
가 아니라 react-dom
에서 가져와야 합니다. 또한 상위의 폼이 위에서 설명한 action
속성을 사용할 때만 작동합니다.
useFormState
와 함께 이 훅을 사용하면 컴포넌트를 불필요한 컨텍스트나 이펙트로 복잡하게 만들지 않으면서도 클라이언트 사이드 폼의 사용자 경험을 향상시킬 수 있습니다. React 문서에서 useFormStatus
훅에 대해 더 자세히 읽을 수 있습니다.
액션이 제출되는 동안 사용자 인터페이스를 낙관적으로(optimistically) 업데이트할 수 있는 새로운 훅입니다.
import { useOptimistic } from 'react';
function AppContainer() {
const [optimisticState, addOptimistic] = useOptimistic(
state,
// 업데이트 함수
(currentState, optimisticValue) => {
// 현재 상태에 낙관적인 값을 합치고 새로운 상태를 반환
},
);
}
위의 장바구니 예제에서는 이 훅을 사용하여 AJAX 호출이 완료되기 전에 새 항목이 추가된 장바구니를 보여줄 수 있습니다.
UI를 낙관적으로 업데이트하는 것은 웹 앱의 사용자 경험을 향상시키는 좋은 방법입니다. 이 훅은 이러한 경우에 많은 도움이 됩니다. useOptimistic
훅에 대한 자세한 내용은 React 문서에서 확인할 수 있습니다.
React 트랜지션 API를 사용하면 UI를 차단하지 않으면서 상태를 업데이트할 수 있습니다. 예를 들어, 사용자가 마음이 바뀌면 이전의 상태 변경을 취소할 수 있게 합니다.
이 아이디어는 startTransition
호출로 상태 변경을 감싸는 것입니다.
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
// setTab(nextTab) 대신에 트랜지션에 상태 변경을 넣음
startTransition(() => {
setTab(nextTab);
});
}
// ...
}
다음 예제는 이 Transitions API를 사용한 탭 탐색을 보여줍니다. "Posts"를 클릭한 다음 즉시 "Contact"를 클릭하세요. 이때 "Posts"의 느린 렌더링이 중단되는 것을 주목하세요. "Contact" 탭이 바로 보입니다. 이 상태 업데이트가 트랜지션으로 표시되었기 때문에 느린 리렌더링이 사용자 인터페이스를 멈추지 않았습니다.
useTransition
훅은 이미 React 18.2에서 사용 가능합니다. React 19에서 새롭게 추가된 기능은 이제 startTransition
에 비동기 함수를 전달할 수 있다는 것입니다. 이 함수는 React에 의해 대기되어 트랜지션을 시작합니다.
이는 AJAX 호출을 통해 데이터를 제출하고 결과를 트랜지션으로 렌더링하는 데 유용합니다. 트랜지션 대기 상태는 비동기 데이터 제출과 함께 시작됩니다. 이미 위에서 설명한 폼 액션 기능에서 사용되고 있습니다. React가 startTransition
으로 감싸진 <form action>
핸들러를 호출하여 현재 페이지를 차단하지 않도록 하기 때문입니다.
이 기능은 아직 React 문서에 작성되지 않았지만, 관련 풀 리퀘스트에서 더 자세히 읽을 수 있습니다.
위에서 설명한 모든 기능은 클라이언트 전용 React 앱에서 작동합니다. Vite로 번들링된 앱에서도 사용할 수 있다는 의미입니다. 새로운 기능을 사용하기 위해 Next나 Remix와 같은 SSR 프레임워크가 필요한 건 아니지만 서버 통합형 React 앱에서도 작동합니다.
새로운 기능의 등장으로 React에서 데이터 페칭과 폼 처리가 상당히 쉬워집니다. 그러나 훅을 통합하여 훌륭한 사용자 경험을 만드는 것은 복잡할 수 있습니다. 대안으로 react-admin과 같은 프레임워크를 사용할 수 있으며, 낙관적 업데이트가 내장된 사용자 친화적인 폼을 제공합니다.
이 기능들이 React 19 대신 React 18.3에 나오지 않는 이유는 몇 가지 작은 변경사항이 포함되어 있기 때문으로 보입니다.
React 19는 언제 출시될까요? 아직 정확한 출시일은 없지만 이 게시물에서 언급된 모든 기능이 이미 작동하고 있습니다. 그러나 아직 canary 릴리즈를 프로덕션 환경에서 사용하는 것은 좋은 생각은 아닌 것 같습니다(Next.js가 사용하고 있다 하더라도).
React 코어 팀이 SSR 앱에서 작업하는 개발자뿐만 아니라 모든 React 개발자의 개발 경험을 향상시키기 위해 노력하고 있는 것을 보니 정말 좋습니다. 또한 데이터 페칭과 폼 처리는 아주 일반적인 문제들이었는데, 새로운 기능을 보니 커뮤니티 피드백을 듣고 있는 것으로 보입니다.
이러한 기능이 React의 안정적인 릴리즈에서 제공되기를 기대합니다!