React 18 Concurrent Rendering 도입 후기
배경
회사 대시보드 프로젝트가 React 17.0.2를 사용 중이었다. 데이터 테이블이 많아 렌더링 성능이 이슈였고, React 18의 Concurrent Features가 도움이 될 것 같아 업그레이드를 진행했다.
마이그레이션 과정
1. ReactDOM.render → 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 />);
2. Automatic Batching 영향
React 18부터는 Promise, setTimeout 내부에서도 자동으로 배칭이 된다. 기존 코드 중 일부에서 의도적으로 여러 번 렌더링을 유발하던 패턴이 있었는데, 이 부분을 flushSync로 감싸서 해결했다.
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(c => c + 1);
});
setFlag(true); // 별도 렌더링
3. useTransition 활용
검색 필터링이 느린 페이지에 useTransition을 적용했다.
const [isPending, startTransition] = useTransition();
const handleSearch = (query) => {
setQuery(query); // 즉시 반영
startTransition(() => {
setFilteredResults(expensiveFilter(query)); // 지연 가능
});
};
체감상 입력 반응성이 확실히 개선됐다.
주의사항
- Suspense는 아직 data fetching에 공식 지원이 없어 스킵했다
- React Testing Library도 함께 업데이트 필요 (12.x)
- StrictMode에서 useEffect가 두 번 실행되는 건 개발 모드 전용이다
결과
번들 크기는 거의 동일했고, 주요 페이지의 INP(Interaction to Next Paint)가 평균 15% 개선됐다. 큰 breaking change 없이 점진적으로 도입할 수 있어서 만족스러웠다.