React 18 Concurrent 기능 마이그레이션 후기
배경
작년 3월에 출시된 React 18을 이제야 프로덕션에 도입했다. 특히 검색 필터가 많은 대시보드 페이지에서 입력 지연이 심했는데, Concurrent 기능으로 개선할 수 있을 것 같았다.
적용 과정
createRoot 마이그레이션
// Before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// After
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
기존 ReactDOM.render를 createRoot로 교체했다. 이것만으로는 동작에 큰 차이가 없었다.
useTransition 적용
검색 필터 입력 시 3000개 이상의 리스트를 필터링하는 로직이 있었다.
function ProductList() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const handleSearch = (e) => {
const value = e.target.value;
setQuery(value); // 즉시 반영
startTransition(() => {
// 무거운 필터링은 낮은 우선순위로
setFilteredProducts(filterProducts(products, value));
});
};
return (
<>
<input value={query} onChange={handleSearch} />
{isPending && <Spinner />}
<List items={filteredProducts} />
</>
);
}
입력 자체는 즉시 반영되고, 리스트 업데이트는 늦춰진다. isPending으로 로딩 상태도 표시할 수 있어서 UX가 개선됐다.
결과
체감상 입력 지연이 확실히 줄었다. Chrome DevTools로 측정했을 때 키 입력 후 화면 반영까지 200ms에서 50ms로 개선됐다. Suspense와 조합하면 더 효과적일 것 같은데, 아직 데이터 페칭 라이브러리가 완전히 대응하지 않아서 다음 단계로 미뤘다.
주의사항
- Strict Mode에서 이중 렌더링이 더 명확해졌다
- useEffect cleanup이 더 엄격해져서 몇 가지 버그를 발견했다
- 써드파티 라이브러리 중 일부는 아직 React 18 대응이 안 됐다