React 18 useDeferredValue로 검색 입력 최적화하기

문제 상황

사내 상품 검색 페이지에서 사용자가 입력할 때마다 5000개 이상의 항목을 필터링하면서 입력이 버벅이는 현상이 발생했다. debounce를 적용해봤지만 입력 자체는 여전히 느렸고, 사용자 경험이 좋지 않았다.

useDeferredValue 적용

React 18의 useDeferredValue를 사용해 검색어 업데이트를 지연시키는 방식으로 해결했다.

import { useState, useDeferredValue, useMemo } from 'react';

function ProductSearch({ products }) {
  const [searchTerm, setSearchTerm] = useState('');
  const deferredSearchTerm = useDeferredValue(searchTerm);

  const filteredProducts = useMemo(() => {
    return products.filter(product =>
      product.name.toLowerCase().includes(deferredSearchTerm.toLowerCase())
    );
  }, [products, deferredSearchTerm]);

  const isStale = searchTerm !== deferredSearchTerm;

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="상품 검색"
      />
      <div style={{ opacity: isStale ? 0.5 : 1 }}>
        {filteredProducts.map(product => (
          <ProductItem key={product.id} product={product} />
        ))}
      </div>
    </div>
  );
}

결과

입력 반응성이 즉각적으로 개선되었다. React가 입력 이벤트를 우선 처리하고 목록 업데이트를 지연시켜 메인 스레드를 차단하지 않았다. isStale 상태로 시각적 피드백도 제공할 수 있었다.

debounce와 달리 타이머를 직접 관리할 필요가 없고, React의 동시성 기능과 자연스럽게 통합되는 점이 좋았다. 다만 React 18 전용이라 프로젝트 업그레이드가 선행되어야 했다.