useTransition vs useDeferredValue — 둘이 정확히 무엇이 다른가
두 훅의 핵심 차이는 받는 것이 함수냐 값이냐. 탭 전환·검색 결과·라우트 이동 등 시나리오별 선택 기준과 같이 쓰는 패턴까지.
React 18에서 추가된 useTransition과 useDeferredValue는 둘 다 "느린 업데이트가 UI를 막지 않게" 하는 도구입니다. 비슷해 보여서 혼용하기 쉽지만, 무엇을 받는가가 다르고 그래서 어디에 쓰는가도 다릅니다.
1. 핵심 차이 한 줄
| 훅 | 받는 것 | 알맞은 곳 |
|---|---|---|
useTransition | 업데이트를 일으킬 함수 | 상태를 직접 변경하는 곳 (이벤트 핸들러) |
useDeferredValue | 외부에서 받은 값 | prop으로 들어온 값을 지연시키고 싶을 때 |
2. useTransition — 상태 변경을 "급하지 않은 것"으로 표시
1const [tab, setTab] = useState('home'); 2const [isPending, startTransition] = useTransition(); 3 4return ( 5 <> 6 <button onClick={() => startTransition(() => setTab('feed'))}> 7 Feed 8 </button> 9 {isPending && <Spinner />} 10 <Tab name={tab} /> 11 </> 12);
startTransition 안에서 일어난 상태 변경은 "급하지 않다"고 표시되어, 사용자 입력 같은 급한 업데이트가 끼어들면 양보합니다. isPending은 그 동안 boolean으로 true가 되어 스피너 표시에 활용됩니다.
3. useDeferredValue — 외부 값을 지연
prop으로 받은 값이나 이미 변경된 state를 사용할 때 — 직접 setState를 부르지 않습니다.
1function SearchResults({ query }) { 2 const deferredQuery = useDeferredValue(query); 3 // query가 새로 들어와도 deferredQuery는 잠시 이전 값 유지 4 const results = useMemo(() => searchHeavy(deferredQuery), [deferredQuery]); 5 return <List results={results} />; 6}
query가 계속 바뀌어도 deferredQuery는 이전 값을 잠시 유지하다가 시스템이 여유 있을 때 따라옵니다. 부모 컴포넌트나 외부 라이브러리에서 값이 들어오는 경우에 사용합니다.
4. 같이 쓰는 패턴
검색창처럼 입력값과 결과 렌더가 분리된 곳:
1function Search() { 2 const [query, setQuery] = useState(''); 3 const deferredQuery = useDeferredValue(query); 4 5 return ( 6 <> 7 <input value={query} onChange={(e) => setQuery(e.target.value)} /> 8 {/* 입력은 즉시 반영 (query) */} 9 {/* 결과는 살짝 지연 (deferredQuery) */} 10 <SearchResults query={deferredQuery} /> 11 </> 12 ); 13}
query로 인풋이 끊김 없이 갱신되고, deferredQuery로 무거운 결과 렌더가 양보합니다.
5. 무엇이 일어나는가
두 훅 모두 React의 concurrent rendering을 활용합니다.
- "이 업데이트는 양보 가능해" 표시
- 사용자 입력(클릭, 타이핑) 같은 급한 일이 들어오면 진행 중인 양보 가능 업데이트를 잠시 멈춤
- 여유가 생기면 마저 진행
결과적으로 큰 리스트 렌더 같은 무거운 작업 중에도 입력이 끊기지 않습니다.
6. 주의할 점
- 두 훅 모두 무거운 작업 자체를 빨리 만들지 않습니다. 단지 "급하지 않다"고 표시할 뿐입니다. 진짜 빠르려면
useMemo, virtualization, 데이터 분할이 필요합니다. startTransition안에서 promise를 await할 수 있게 React 19에서 확장됐습니다. Suspense와 자연스럽게 결합.useDeferredValue는 React 메모이제이션과 짝입니다.useMemo/React.memo없이 쓰면 효과가 거의 없습니다.
7. 정리
| 시나리오 | 추천 |
|---|---|
| 탭 전환, 라우트 이동 | useTransition |
| 검색 결과 렌더 | useDeferredValue |
| 부모에서 받은 값을 지연 | useDeferredValue |
| 클릭 핸들러에서 상태 갱신 | useTransition |
내가 setState를 부르냐, 외부에서 값이 들어오냐만 구분하면 어느 쪽인지 분명해집니다.
참고: React 공식 문서 — useTransition / useDeferredValue.