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

개발 에러 해결 가이드 - FixLog 노트

React Maximum update depth exceeded 에러란?

React 개발 중 ‘Maximum update depth exceeded’ 에러를 만난 적이 있나요? 이 에러는 React 컴포넌트에서 무한 루프가 발생할 때 나타나는 경고 메시지입니다. 컴포넌트가 렌더링될 때마다 state가 업데이트되고, 이 업데이트가 다시 렌더링을 유발하면서 끝없이 반복되는 상황에서 발생합니다. React는 성능 보호를 위해 업데이트 깊이에 제한을 두고 있으며, 이 제한을 초과하면 에러를 발생시킵니다. 초보 개발자부터 숙련된 개발자까지 흔히 마주치는 이 문제를 철저히 분석하고 해결 방법을 알아보겠습니다.

🤖 AI 에러 분석 도우미

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

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

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

에러 상세 분석

Maximum update depth exceeded 에러의 정확한 메시지는 다음과 같습니다: “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회)를 초과했을 때 발생합니다.

에러가 발생하는 핵심 메커니즘은 다음과 같습니다. 컴포넌트가 렌더링되면서 state 업데이트 함수를 호출하고, 이 업데이트가 즉시 재렌더링을 트리거합니다. 재렌더링 과정에서 다시 동일한 state 업데이트가 발생하면 무한 루프에 빠지게 됩니다. React는 이러한 무한 루프를 감지하여 애플리케이션이 멈추는 것을 방지하기 위해 에러를 발생시킵니다. 브라우저 콘솔에서 스택 추적을 확인하면 어떤 컴포넌트와 함수에서 문제가 발생했는지 파악할 수 있습니다.

에러 발생 원인 5가지

1. 렌더링 중 직접 setState 호출

가장 흔한 원인은 컴포넌트의 렌더링 함수 본문에서 직접 setState를 호출하는 경우입니다. 렌더링될 때마다 state가 변경되고 다시 렌더링이 발생하여 무한 루프가 생성됩니다. 예를 들어 return 문 위에서 setState를 호출하거나, JSX 내부에서 state 업데이트 함수를 직접 실행하는 경우입니다.

2. 이벤트 핸들러 함수 즉시 실행

onClick이나 onChange 같은 이벤트 핸들러에 함수를 전달할 때, 함수 참조 대신 함수를 즉시 실행하는 실수를 자주 합니다. onClick={handleClick()}처럼 괄호를 붙이면 렌더링될 때마다 함수가 실행되어 state가 변경되고 재렌더링이 발생합니다.

3. useEffect 의존성 배열 누락

useEffect 훅에서 의존성 배열을 잘못 설정하면 무한 루프가 발생할 수 있습니다. 특히 useEffect 내부에서 state를 업데이트하는데, 해당 state를 의존성 배열에 포함시키면 업데이트 → 재실행 → 업데이트의 무한 루프에 빠집니다.

4. 부모 컴포넌트에서 매번 새로운 객체/배열 생성

부모 컴포넌트가 자식 컴포넌트에 props로 객체나 배열을 전달할 때, 렌더링마다 새로운 참조를 생성하면 문제가 발생합니다. 자식 컴포넌트가 useEffect에서 이 props를 의존성으로 사용하면 매번 새로운 참조로 인식되어 무한 루프가 발생할 수 있습니다.

5. componentDidUpdate나 useEffect에서 조건 없이 state 업데이트

클래스 컴포넌트의 componentDidUpdate나 함수형 컴포넌트의 useEffect에서 조건문 없이 state를 업데이트하면 업데이트가 계속 반복됩니다. 적절한 조건 검사나 의존성 관리 없이 state 변경 로직을 작성하면 이런 문제가 발생합니다.

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

해결법 1: 이벤트 핸들러 함수 참조로 전달

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

// 잘못된 방법
function Counter() {
  const [count, setCount] = useState(0);
  return ; // ❌
}

// 올바른 방법
function Counter() {
  const [count, setCount] = useState(0);
  return ; // ✅
}

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

필요한 의존성만 포함하고, 무한 루프를 방지합니다.

// 잘못된 방법
function UserProfile() {
  const [user, setUser] = useState({});
  
  useEffect(() => {
    setUser({ ...user, timestamp: Date.now() }); // ❌ 무한 루프
  }, [user]);
}

// 올바른 방법
function UserProfile() {
  const [user, setUser] = useState({});
  
  useEffect(() => {
    // 초기화 시에만 실행
    setUser({ name: 'John', timestamp: Date.now() });
  }, []); // ✅ 빈 배열로 마운트 시에만 실행
}

해결법 3: 렌더링 로직에서 setState 제거

state 업데이트는 이벤트 핸들러나 useEffect에서만 수행합니다.

// 잘못된 방법
function BadComponent() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // ❌ 렌더링 중 setState
  return 
{count}
; } // 올바른 방법 function GoodComponent() { const [count, setCount] = useState(0); useEffect(() => { setCount(1); // ✅ useEffect 내부에서 실행 }, []); return
{count}
; }

해결법 4: useMemo와 useCallback 활용

객체나 함수의 참조를 메모이제이션하여 불필요한 재생성을 방지합니다.

function ParentComponent() {
  const [data, setData] = useState([]);
  
  // 매번 새로운 객체 생성 방지
  const config = useMemo(() => ({
    theme: 'dark',
    size: 'large'
  }), []); // ✅
  
  // 함수 참조 유지
  const handleUpdate = useCallback((newData) => {
    setData(newData);
  }, []); // ✅
  
  return ;
}

해결법 5: 함수형 업데이트 사용

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

function Counter() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    // 함수형 업데이트로 최신 값 보장
    setCount(prevCount => prevCount + 1); // ✅
  };
  
  return ;
}

해결법 6: 조건부 state 업데이트

componentDidUpdate나 useEffect에서 조건을 검사한 후 업데이트합니다.

function DataFetcher({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    // userId가 변경될 때만 fetch
    if (userId) {
      fetchUser(userId).then(data => setUser(data)); // ✅
    }
  }, [userId]); // userId 변경 시에만 실행
  
  return 
{user?.name}
; }

해결법 7: React.memo로 불필요한 재렌더링 방지

자식 컴포넌트를 메모이제이션하여 props가 변경되지 않으면 재렌더링을 방지합니다.

// 자식 컴포넌트 메모이제이션
const ChildComponent = React.memo(({ data, onUpdate }) => {
  useEffect(() => {
    // data가 실제로 변경될 때만 실행
    console.log('Data updated:', data);
  }, [data]);
  
  return 
{data.value}
; }); // ✅ function ParentComponent() { const [count, setCount] = useState(0); const data = useMemo(() => ({ value: count }), [count]); return ; }

예방법과 베스트 프랙티스

1. 이벤트 핸들러 명명 규칙 준수

이벤트 핸들러는 ‘handle’로 시작하는 명확한 이름을 사용하고, JSX에서는 반드시 함수 참조로 전달합니다. 화살표 함수나 bind를 사용할 때도 렌더링마다 새로운 함수가 생성되지 않도록 useCallback을 활용합니다.

2. useEffect 의존성 배열 린터 활성화

ESLint의 react-hooks/exhaustive-deps 규칙을 활성화하여 의존성 배열을 올바르게 관리합니다. 이 린터는 누락된 의존성이나 불필요한 의존성을 자동으로 감지해줍니다.

3. State 업데이트 위치 명확히 하기

state 업데이트는 이벤트 핸들러, useEffect, useCallback 내부에서만 수행하고, 렌더링 함수의 본문이나 JSX 내부에서는 절대 직접 호출하지 않습니다. 이 규칙을 팀 코딩 컨벤션으로 정립합니다.

4. 개발자 도구 활용

React Developer Tools의 Profiler를 사용하여 불필요한 재렌더링을 감지하고, 컴포넌트의 렌더링 패턴을 분석합니다. 무한 루프가 의심되면 콘솔의 스택 추적을 자세히 확인합니다.

5. 코드 리뷰와 테스트

팀원 간 코드 리뷰를 통해 state 업데이트 로직을 검증하고, 단위 테스트에서 렌더링 횟수를 검사하는 테스트를 추가합니다. React Testing Library의 render 호출 횟수를 모니터링할 수 있습니다.

마무리

Maximum update depth exceeded 에러는 React에서 자주 발생하지만, 원인을 정확히 이해하면 쉽게 해결할 수 있습니다. 이벤트 핸들러를 올바르게 전달하고, useEffect 의존성 배열을 정확히 설정하며, 렌더링 로직과 state 업데이트 로직을 명확히 분리하는 것이 핵심입니다. useMemo와 useCallback을 적절히 활용하고, 함수형 업데이트 패턴을 사용하면 대부분의 무한 루프 문제를 예방할 수 있습니다. 이 가이드를 참고하여 깔끔하고 안정적인 React 애플리케이션을 개발하시기 바랍니다. 문제가 지속되면 React DevTools로 렌더링 패턴을 분석하고, 스택 추적을 통해 정확한 원인을 파악하세요.

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

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

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

5

React Hook useEffect has a missing dependency 완벽 해결법 – 원인부터 예방까지

📂 React 에러
📅 2025. 11. 22.
🎯 React Hook useEffect has a missing dependency

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

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

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


📘 페이스북


🐦 트위터


✈️ 텔레그램

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

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

여러분은 Maximum update depth exceeded에 대해 어떻게 생각하시나요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

📱 전체 버전 보기