금요일

React useEffect 무한루프 원인과 해결 방법

🔍 검색 키워드: React useEffect 무한루프, useEffect 의존성 배열, useEffect 성능 최적화, 리액트 렌더링 최적화, useEffect 클린업 함수

React useEffect 무한루프 원인과 해결 방법

useEffect는 React의 핵심 Hook이지만, 잘못 사용하면 무한 루프에 빠져 성능 저하와 메모리 누수를 유발합니다. 특히 의존성 배열을 제대로 관리하지 않으면 예상치 못한 사이드 이펙트가 반복됩니다.

문제 증상

콘솔에서 다음과 같은 현상이 반복됩니다:

GET /api/data 200 (무한 반복)
Warning: Can't perform a React state update on an unmounted component
Memory usage: 500MB → 1.5GB (급격히 증가)

브라우저 탭이 점점 느려지고, 개발자 도구의 Network 탭에서 같은 요청이 계속 쌓입니다.

원인 분석

원인 1: 의존성 배열 누락

useEffect(() => {
  fetchData();
}, []); // ✗ 의존성 배열이 비어있음

이 경우 useEffect는 마운트 시점에만 실행되지만, fetchData 내부에서 setState를 호출하면:

  • 상태가 변경됨 → 리렌더링 → useEffect 다시 실행 (만약 의존성이 제대로 안 되어 있으면)

원인 2: 객체/배열을 의존성으로 사용

const config = { url: '/api/data' };

useEffect(() => {
  fetch(config.url);
}, [config]); // ✗ 객체는 매번 새로 생성되므로 무한루프

원인 3: 상태를 의존성에 포함 + 그 상태를 변경

const [count, setCount] = useState(0);

useEffect(() => {
  setCount(count + 1); // ✗ count가 변경 → useEffect 실행 → count 변경 → 무한루프
}, [count]);

해결 방법

해결책 1: 의존성 배열 올바르게 설정

useEffect(() => {
  fetchData();
}, []); // 마운트 시점에만 1회 실행

특정 값이 변경될 때만 실행:

const [userId, setUserId] = useState(null);

useEffect(() => {
  if (userId) {
    fetchUserData(userId);
  }
}, [userId]); // userId 변경 시에만 실행

해결책 4: 클린업 함수로 이전 요청 취소

useEffect(() => {
  const controller = new AbortController();

  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(data => setData(data))
    .catch(err => {
      if (err.name !== 'AbortError') {
        console.error(err);
      }
    });

  return () => controller.abort();
}, []);

체크리스트 및 정리표

상황결과
마운트 시점에만 실행1회 실행 ✓
특정 값 변경 시 실행value 변경 시마다 실행 ✓

이 패턴들을 적용하면 useEffect 무한루프 문제를 대부분 해결할 수 있습니다.

댓글 없음:

댓글 쓰기