React 18 RC의 Concurrent Rendering 미리 살펴보기

배경

프로젝트에서 대용량 리스트 렌더링 시 입력 필드가 버벅이는 문제가 있었다. React 18 베타가 공개되면서 Concurrent Features를 통해 이 문제를 해결할 수 있을 것 같아 테스트 환경에 적용해봤다.

startTransition 적용

검색어 입력 시 필터링되는 리스트가 3000개 이상일 때 입력 딜레이가 발생했다. startTransition으로 상태 업데이트 우선순위를 분리했다.

import { startTransition } from 'react';

function SearchList() {
  const [query, setQuery] = useState('');
  const [deferredQuery, setDeferredQuery] = useState('');

  const handleChange = (e) => {
    setQuery(e.target.value); // 즉시 반영
    startTransition(() => {
      setDeferredQuery(e.target.value); // 낮은 우선순위
    });
  };

  const filteredItems = useMemo(
    () => items.filter(item => item.name.includes(deferredQuery)),
    [deferredQuery]
  );

  return (
    <>
      <input value={query} onChange={handleChange} />
      <List items={filteredItems} />
    </>
  );
}

입력 필드는 즉시 반응하고, 리스트 렌더링은 뒤로 밀려나면서 UX가 개선됐다.

useDeferredValue 활용

위 패턴을 Hook으로 단순화할 수 있었다.

import { useDeferredValue } from 'react';

function SearchList() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  const filteredItems = useMemo(
    () => items.filter(item => item.name.includes(deferredQuery)),
    [deferredQuery]
  );

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <List items={filteredItems} />
    </>
  );
}

주의사항

  • 아직 베타 버전이라 프로덕션 적용은 보류
  • Suspense for Data Fetching은 여전히 실험 단계
  • 기존 코드는 대부분 호환되지만, useEffect 타이밍이 일부 변경됨

정식 릴리스가 나오면 점진적으로 도입할 계획이다.

React 18 RC의 Concurrent Rendering 미리 살펴보기