React 18 useTransition으로 검색 UI 개선하기

문제 상황

관리자 페이지에서 5000개 이상의 상품 목록을 검색하는 기능이 있었다. 검색어를 입력할 때마다 목록을 필터링하는데, 타이핑이 버벅이는 문제가 발생했다. debounce를 적용해봤지만 근본적인 해결은 아니었다.

useTransition 적용

React 18로 업그레이드하면서 useTransition을 시도해봤다.

import { useState, useTransition } from 'react';

function ProductSearch({ products }) {
  const [searchTerm, setSearchTerm] = useState('');
  const [filteredProducts, setFilteredProducts] = useState(products);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e) => {
    const value = e.target.value;
    setSearchTerm(value); // 즉시 업데이트

    startTransition(() => {
      // 무거운 필터링 작업은 낮은 우선순위로
      const filtered = products.filter(p => 
        p.name.toLowerCase().includes(value.toLowerCase())
      );
      setFilteredProducts(filtered);
    });
  };

  return (
    <div>
      <input 
        value={searchTerm} 
        onChange={handleSearch}
        placeholder="상품 검색..."
      />
      {isPending && <span>검색 중...</span>}
      <ProductList products={filteredProducts} />
    </div>
  );
}

결과

입력 필드는 즉각 반응하고, 목록 렌더링은 백그라운드에서 처리되어 타이핑이 끊기지 않았다. isPending을 활용해 로딩 상태도 표시할 수 있었다.

debounce와 달리 React가 자동으로 우선순위를 관리해주니 코드가 더 직관적이었다. 다만 여전히 초기 렌더링 비용은 존재하므로, 가상화(react-window)를 함께 적용하는 것도 고려 중이다.

참고사항

  • React 18.0 이상 필요
  • Concurrent 기능이므로 ReactDOM.createRoot 사용 필수
  • 모든 상황에 만능은 아니고, 실제 무거운 연산에 적용해야 효과가 있음