React 18 업그레이드 후 렌더링 이슈 해결

문제 상황

회사 프로젝트를 React 18로 업그레이드하면서 예상치 못한 렌더링 이슈들이 발생했다. 특히 개발 환경에서 useEffect가 두 번 실행되는 현상과 SSR 환경에서 hydration 에러가 빈번하게 나타났다.

useEffect 중복 실행

React 18의 Strict Mode에서는 컴포넌트를 의도적으로 두 번 마운트한다. 이는 향후 추가될 기능을 대비한 것인데, 기존 코드에서 cleanup 함수를 제대로 작성하지 않은 부분들이 드러났다.

// 문제 코드
useEffect(() => {
  const subscription = api.subscribe();
  // cleanup 없음
}, []);

// 수정
useEffect(() => {
  const subscription = api.subscribe();
  return () => subscription.unsubscribe();
}, []);

Hydration 에러

SSR로 렌더링된 HTML과 클라이언트 렌더링 결과가 달라서 발생했다. 주로 Date 객체나 랜덤 값을 사용하는 부분이 문제였다.

// 문제: 서버/클라이언트 불일치
const timestamp = new Date().toISOString();

// 해결: useEffect로 클라이언트에서만 렌더링
const [timestamp, setTimestamp] = useState(null);
useEffect(() => {
  setTimestamp(new Date().toISOString());
}, []);

Automatic Batching

React 18의 automatic batching 덕분에 여러 상태 업데이트가 자동으로 배치 처리되어 렌더링 횟수가 줄었다. 기존에 수동으로 처리하던 최적화 코드를 제거할 수 있었다.

// React 17: 두 번 렌더링
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f); // 별도 렌더링
}, 1000);

// React 18: 자동으로 한 번만 렌더링

결론

Strict Mode의 동작 변경으로 기존에 숨어있던 버그들을 발견할 수 있었다. cleanup 함수를 빠짐없이 작성하고, SSR 환경에서는 서버/클라이언트 렌더링 일관성을 더 신경써야 한다. Concurrent 기능은 아직 적용하지 않았지만, 기반은 마련된 상태다.

React 18 업그레이드 후 렌더링 이슈 해결