Maximum update depth exceeded 완벽 해결법 – 원인부터 예방까지

Maximum update depth exceeded 에러 완벽 해결 가이드

도입 – Maximum update depth exceeded 에러란?

React 개발 중 “Maximum update depth exceeded” 에러를 마주한 적이 있나요? 이 에러는 React 애플리케이션에서 가장 흔하게 발생하는 무한 루프 관련 오류 중 하나입니다. 컴포넌트가 렌더링 중에 상태를 계속해서 업데이트하면서 무한 렌더링 사이클에 빠질 때 React가 이를 감지하고 발생시키는 보호 메커니즘입니다. 이 에러가 발생하면 애플리케이션이 멈추고 사용자 경험이 크게 저하됩니다. 특히 초보 개발자들이 이벤트 핸들러나 useEffect 훅을 잘못 사용할 때 자주 겪게 되는 문제입니다. 이 글에서는 Maximum update depth exceeded 에러의 원인과 해결 방법을 체계적으로 알아보겠습니다.

🤖 AI 에러 분석 도우미

이 에러는 다음과 같은 상황에서 주로 발생합니다:

  • 코드 문법 오류가 있을 때
  • 라이브러리나 의존성 문제
  • 환경 설정이 잘못된 경우
  • 타입 불일치 문제

💡 위 해결법을 순서대로 시도해보세요. 90% 이상 해결됩니다!

에러 상세 분석

이 에러의 정확한 메시지는 “Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.”입니다. React는 내부적으로 업데이트 깊이를 추적하며, 일정 임계값(보통 50회)을 초과하면 무한 루프로 판단하고 에러를 발생시킵니다.

이 에러는 주로 다음과 같은 상황에서 나타납니다. 첫째, 렌더링 과정에서 상태가 변경되는 경우입니다. 둘째, 이벤트 핸들러가 즉시 실행되는 경우입니다. 셋째, useEffect나 라이프사이클 메서드에서 의존성 배열 설정이 잘못된 경우입니다. 이러한 상황들은 모두 컴포넌트가 렌더링될 때마다 다시 상태를 변경하고, 그 상태 변경이 다시 렌더링을 유발하는 악순환을 만듭니다. React는 이를 방지하기 위해 업데이트 횟수를 제한하고 개발자에게 경고합니다.

발생 원인 5가지

1. 이벤트 핸들러 즉시 실행

가장 흔한 원인은 onClick 등의 이벤트 핸들러에서 함수를 호출하는 대신 즉시 실행하는 것입니다. onClick={handleClick()}처럼 괄호를 붙이면 렌더링될 때마다 함수가 실행되어 상태가 변경되고, 이것이 다시 렌더링을 유발합니다.

2. useEffect 의존성 배열 누락

useEffect 훅에서 의존성 배열을 생략하거나 잘못 설정하면 매 렌더링마다 effect가 실행됩니다. 특히 effect 내부에서 상태를 업데이트하는 경우 무한 루프에 빠지게 됩니다.

3. 렌더링 중 상태 업데이트

컴포넌트 본문(render 함수나 함수 컴포넌트 내부)에서 직접 setState나 상태 업데이트 함수를 호출하면 렌더링이 완료되기 전에 다시 렌더링이 시작되어 무한 루프가 발생합니다.

4. componentDidUpdate에서 조건 없는 setState

클래스 컴포넌트의 componentDidUpdate 내부에서 조건문 없이 setState를 호출하면 업데이트가 다시 componentDidUpdate를 호출하는 순환 구조가 만들어집니다.

5. useEffect에서 상태를 의존성으로 설정

useEffect 내부에서 업데이트하는 상태를 의존성 배열에 포함시키면, 상태 변경이 effect를 다시 실행하고 effect가 다시 상태를 변경하는 무한 루프가 발생합니다.

해결방법 7가지 (코드 포함)

방법 1: 이벤트 핸들러 올바르게 전달하기

함수를 즉시 실행하지 않고 참조만 전달해야 합니다.

// 잘못된 예시 - 렌더링 시 즉시 실행됨
function Counter() {
  const [count, setCount] = useState(0);
  return ;
}

// 올바른 예시 1 - 화살표 함수 사용
function Counter() {
  const [count, setCount] = useState(0);
  return ;
}

// 올바른 예시 2 - 함수 참조 전달
function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => setCount(count + 1);
  return ;
}

방법 2: useEffect 의존성 배열 올바르게 설정

effect가 실행되어야 할 조건을 의존성 배열에 정확히 명시해야 합니다.

// 잘못된 예시 - 의존성 배열 누락
function DataFetcher() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetchData().then(setData);
  }); // 매 렌더링마다 실행됨
  return 
{data}
; } // 올바른 예시 - 빈 배열로 마운트 시에만 실행 function DataFetcher() { const [data, setData] = useState(null); useEffect(() => { fetchData().then(setData); }, []); // 컴포넌트 마운트 시 한 번만 실행 return
{data}
; }

방법 3: 함수형 업데이트 사용

이전 상태를 기반으로 업데이트할 때는 함수형 업데이트를 사용합니다.

// 잘못된 예시
function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(count + 1);
  }, [count]); // count가 변경될 때마다 실행되어 무한 루프
  return 
{count}
; } // 올바른 예시 function Counter() { const [count, setCount] = useState(0); const increment = () => { setCount(prev => prev + 1); // 함수형 업데이트 }; return ; }

방법 4: useCallback으로 함수 메모이제이션

의존성 배열에 함수를 포함해야 할 때는 useCallback을 사용합니다.

function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  const search = useCallback(() => {
    api.search(query).then(setResults);
  }, [query]); // query가 변경될 때만 함수 재생성
  
  useEffect(() => {
    search();
  }, [search]); // search 함수를 안전하게 의존성으로 사용
  
  return 
{results.map(r =>
{r.name}
)}
; }

방법 5: componentDidUpdate에서 조건문 사용

클래스 컴포넌트에서는 반드시 조건을 확인한 후 setState를 호출해야 합니다.

// 잘못된 예시
class MyComponent extends React.Component {
  componentDidUpdate() {
    this.setState({ updated: true }); // 무한 루프 발생
  }
}

// 올바른 예시
class MyComponent extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.userId !== prevProps.userId) {
      this.setState({ updated: true }); // 조건부 업데이트
    }
  }
}

방법 6: useRef로 렌더링 없이 값 저장

렌더링을 유발하지 않고 값을 저장해야 할 때는 useRef를 사용합니다.

function Timer() {
  const [count, setCount] = useState(0);
  const intervalRef = useRef();
  
  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);
    
    return () => clearInterval(intervalRef.current);
  }, []); // 빈 배열로 한 번만 실행
  
  return 
{count}
; }

방법 7: useMemo로 계산 결과 캐싱

복잡한 계산 결과를 캐싱하여 불필요한 재계산과 렌더링을 방지합니다.

function ExpensiveComponent({ data }) {
  const processedData = useMemo(() => {
    return data.map(item => complexCalculation(item));
  }, [data]); // data가 변경될 때만 재계산
  
  return 
{processedData.map(d =>
{d.value}
)}
; }

예방법과 베스트 프랙티스

1. 이벤트 핸들러는 항상 참조로 전달

onClick, onChange 등의 이벤트 핸들러에는 함수 참조를 전달하고, 괄호를 붙여 즉시 실행하지 않도록 주의합니다.

2. useEffect 의존성 배열 철저히 관리

ESLint의 react-hooks/exhaustive-deps 규칙을 활성화하여 의존성 배열 누락을 방지합니다. 모든 외부 값은 의존성 배열에 포함시키되, 무한 루프가 발생하지 않도록 설계합니다.

3. 렌더링 중에는 상태 변경 금지

컴포넌트 본문에서는 절대 setState나 상태 업데이트 함수를 직접 호출하지 않습니다. 상태 변경은 이벤트 핸들러나 useEffect 내부에서만 수행합니다.

4. React DevTools 활용

React DevTools의 Profiler를 사용하여 불필요한 리렌더링을 모니터링하고 최적화합니다. 컴포넌트가 예상보다 많이 렌더링되는지 확인할 수 있습니다.

5. 코드 리뷰와 테스트

팀원과의 코드 리뷰를 통해 무한 루프 가능성을 사전에 발견하고, 단위 테스트를 작성하여 상태 업데이트 로직이 올바르게 동작하는지 검증합니다.

마무리

Maximum update depth exceeded 에러는 React 개발에서 흔히 발생하지만, 원인을 이해하면 쉽게 해결할 수 있습니다. 이벤트 핸들러를 올바르게 전달하고, useEffect의 의존성 배열을 정확히 설정하며, 렌더링 중 상태 변경을 피하는 것이 핵심입니다. 이 글에서 소개한 7가지 해결 방법과 베스트 프랙티스를 따르면 무한 루프 문제를 예방하고 안정적인 React 애플리케이션을 개발할 수 있습니다. 에러가 발생하면 당황하지 말고 체계적으로 원인을 분석하여 해결하시기 바랍니다.

📚 함께 읽으면 좋은 글

1

Maximum update depth exceeded 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 11. 8.
🎯 Maximum update depth exceeded

2

Maximum update depth exceeded 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 11. 4.
🎯 Maximum update depth exceeded

3

Maximum update depth exceeded 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 11. 3.
🎯 Maximum update depth exceeded

4

Maximum update depth exceeded 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 10. 22.
🎯 Maximum update depth exceeded

5

Cannot read properties of undefined (reading ‘map’) 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 11. 6.
🎯 Cannot read properties of undefined (reading ‘map’)

💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!

📢 이 글이 도움되셨나요? 공유해주세요!

여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨

🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏

💬 여러분의 소중한 의견을 들려주세요!

이 글에서 가장 도움이 된 부분은 어떤 것인가요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨

🔔 블로그 구독하고 최신 글을 받아보세요!

📚
다양한 주제
17개 카테고리

정기 업데이트
하루 3회 발행

🎯
실용적 정보
바로 적용 가능

💡
최신 트렌드
2025년 기준

🌟 React 에러부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨

📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!

답글 남기기