목차
useDeferredValue 훅
useDeferredValue는 값의 변경을 지연시켜 UI 반응성을 높여주는 훅이다. 예를 들어, 검색창에 글자를 입력할 때마다 무거운 연산이 필요한 컴포넌트를 렌더링할 때 버벌일 수 있는데, 이럴 때 useDefferedValue 훅을 사용하면 입력값이 즉시 반영된다. 리스트 렌더링은 살짝 느리게(지연)해서 사용자는 부드럽게 입력할 수 있고, 리스트는 천천히 업데이트돼서 전체 앱의 반응성이 좋아진다.
아래 예시와 같이 검색 입력과 필터링에 오랜 시간이 소요되는 리스트 렌더링의 UI 반응성을 향상시킬 수 있다. 입력값 등 즉각적으로 반영되어야 하는 UI와 해당 값에 의존하는 무거운 컴포넌트(예: 복잡하고 무거운 리스트 렌더링, 필터링 등)의 업데이트를 분리할 수 있다.
import { useState, useDeferredValue } from 'react'
export default function SearchComponent() {
// 입력값은 즉시 반영되어 사용자는 입력이 버벅이지 않음
const [query, setQuery] = useState('')
// 무거운 리스트는 값이 살짝 늦게(지연) 업데이트되어 전체 앱의 반응성이 좋아짐
const deferredQuery = useDeferredValue(query)
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<HeavyList filter={deferredQuery} />
</>
)
}
useDefferedValue 훅은 별도의 로딩 상태를 제공하지 않지만, 지연된 값과 실제 값이 다르면 최신 값으로 업데이트 중임을 알 수 있다.
다음은 useDefferedValue 훅을 사용할 때 고려해야 할 사항이다.
- 실제 성능 병목 지점 파악: 입력과 동시에 렌더링되는 컴포넌트가 정말 성능 문제를 일으키는지 먼저 확인해야 한다. 느린 컴포넌트가 반드시 즉시 렌더링되어야 하는지, 아니면 입력과 분리해도 되는지 구조를 점검하는 것이 중요하다.
- 입력과 결과 렌더링의 분리: 입력은 즉시 반영하고, 결과(검색, 필터링)는 나중에 보여주는 UX가 대용량 데이터 처리에 효과적이다. 검색창, 필터링, 대용량 테이블 등에서 특히 유용하다.
- UI 우선순위 자동 조정: useDefferedValue를 사용하면 리액트가 빠른 작업(입력)과 느린 작업(렌더링)의 우선순위를 자동으로 조정한다. 쓰로틀(throttle)이나 디바운스(debounce) 없이 자연스럽고 부드러운 사용자 경험을 만들 수 있다.
- 이전 데이터 유지: Suspense와 함께 사용하면 새로운 데이터를 불러오는 동안 "로딩 중..." 대신 이전 데이터를 계속 보여줄 수 있다. 데이터가 준비되면 새로운 데이터로 자동 교체되어 UX가 더욱 자연스러워진다.
- 캐싱 전략도 중요: 동일 쿼리에 대해 캐싱을 적용하면 불필요한 네트워크 요청과 렌더링을 줄일 수 있다. getResource와 같은 캐싱 패턴을 활용하면 성능과 사용자 경험이 모두 향상된다.
useTransition 훅
useTransition 훅은 상태 업데이트 자체를 낮은 우선순위로 처리해서 보다 중요한 UI 업데이트(예: 버튼, 입력 등)가 먼저 처리되도록 도와주는 훅이다. 예를 들어, 탭을 전환할 때 특정 탭(예: 댓글 보기)이 매우 느리게 렌더링된다면 앱 전체가 멈춘 것처럼 느껴질 수 있다. 이럴 때 useTransition 훅을 사용하면 탭 전환 버튼 클릭 등 중요한 UI는 즉시 반영된다. 반면, 느린 상태 업데이트는 낮은 우선순위로 처리되어 앱 전체가 멈추지 않고 부드럽게 작동한다.
useDeferredValue와 useTransition 훅의 차이와 용도를 살펴보자.
useDefferedValue 훅 | useTransition 훅 | |
용도 | 값의 변경을 지연시킴 | 상태 업데이트(전체 작업)를 낮은 우선순위로 처리 |
적용 위치 | 입력값, 검색창, 필터 등 | 탭 전환, 페이지 이동, 대량 데이터 렌더링 등 |
UI 반응성 | 입력 등 즉각적 UI와 느린 연산 분리 | 중요한 UI(입력, 버튼 등)와 느린 상태 변화 분리 |
로딩 상태 | 직접 비교해서 표시해야 함 (defferedValue !== value |
isPending으로 트랜지션 상태(로딩 중)를 자동 표시 |
use API(함수)
use 함수는 컴포넌트 내부에서 Promise의 결과를 동기적으로 사용할 수 있게 해주는 React API다. 비동기 데이터(Promise)를 컴포넌트 렌더링 과정에서 직접 사용할 수 있고, Suspense, Error Boundary와 연계해 로딩 UI와 에러 처리를 자동화할 수 있다.
use 함수에 Promise 인스턴스를 전달하면 Promise가 완료될 때까지 컴포넌트 렌더링을 대기한다.
import { use } from 'react'
export default function Users({ promise }) {
const users = use(promise)
return (
<ul>
{
users.map((user) => (
<li key={user.id}>{user.name}</li>
))
}
</ul>
)
}
use 함수는 항상 같은 Promise 인스턴스를 전달해야 하며, 서버 컴포넌트 또는 useMemo 훅을 사용해 한 번만 생성하는 것이 안전하다. 또한, 함수기 때문에 조건문이나 반복문 등 어디서든 사용할 수 있고, 컨텍스트에도 활용할 수 있다.
오늘 하루를 돌아보며
다음 주에 시작할 파이털 프로젝트를 대비해서 사용할 수 있는 훅과 함수를 배우는 중이다. 꼭 프로젝트에서 사용하지 않더라도 나중에 취직해서 사용하게 될 수도 있으니 익혀두면 유용할 것이다. 지금 당장 바로 사용할 수 있을 정도로 이해하지는 못했지만, 어떻게 작동하는지, 언제 사용하는지만 알아도 나중에 필요할 때 떠올릴 수 있을 것이다. 조급해하지 말고, 지금 배우는 것들을 나중에 떠올릴 수 있을 정도만이라도 익숙해지자!