React 18 Concurrent Rendering과 useDeferredValue 적용 후기
문제 상황
상품 목록 페이지에서 검색어 입력 시 약 2000개의 아이템을 필터링하는 기능을 구현했다. 타이핑할 때마다 입력 필드가 버벅이면서 사용자 경험이 매우 나빴다.
function ProductList() {
const [searchText, setSearchText] = useState('');
const filteredProducts = products.filter(p =>
p.name.includes(searchText)
);
return (
<>
<input value={searchText} onChange={e => setSearchText(e.target.value)} />
<ProductGrid products={filteredProducts} />
</>
);
}
입력할 때마다 2000개 아이템을 전부 렌더링하다 보니 메인 스레드가 블로킹되었다.
useDeferredValue 적용
React 18에서 제공하는 useDeferredValue를 사용해 검색 결과 렌더링의 우선순위를 낮췄다.
import { useState, useDeferredValue } from 'react';
function ProductList() {
const [searchText, setSearchText] = useState('');
const deferredSearchText = useDeferredValue(searchText);
const filteredProducts = products.filter(p =>
p.name.includes(deferredSearchText)
);
return (
<>
<input value={searchText} onChange={e => setSearchText(e.target.value)} />
<ProductGrid products={filteredProducts} />
</>
);
}
input의 상태는 즉시 업데이트되고, 필터링 결과는 지연되어 렌더링된다. 사용자는 타이핑이 막히지 않는다고 느낀다.
결과
- 입력 지연 체감 거의 없음
- debounce보다 자연스러운 UX
- Concurrent Rendering 덕분에 React가 알아서 우선순위 조정
기존에 사용하던 lodash debounce는 제거했다. React 18의 동시성 기능이 생각보다 실용적이었다.