본문 바로가기
Frontend2026년 6월 5일3분 읽기

React useEffect 무한 루프, 왜 생기고 어떻게 끊나

YS
김영삼
조회 1086
React useEffect 무한 루프, 왜 생기고 어떻게 끊나

핵심 요약

useEffect 무한 루프는 거의 항상 "이펙트 안에서 상태를 바꾸는데, 그 상태(또는 매 렌더마다 새로 생성되는 객체/함수)가 의존성 배열에 들어 있을 때" 발생한다. 의존성을 원시값으로 좁히거나, 객체·함수를 useMemo/useCallback으로 고정하면 끊긴다.

1. 가장 흔한 원인 3가지

  • 객체/배열 의존성useEffect(fn, [{ id }])처럼 매 렌더마다 새 참조가 생겨 항상 "변경됨"으로 인식
  • 이펙트 안에서 의존하는 상태를 setState — 상태 변경 → 리렌더 → 이펙트 재실행 → 다시 setState
  • 인라인 함수 의존성 — 부모가 넘긴 onChange={() => ...}가 매번 새 함수

2. 해결 패턴

// ❌ data는 매 렌더 새 객체
useEffect(() => { fetchUser(data) }, [data])

// ✅ 실제로 바뀌는 원시값만 의존
useEffect(() => { fetchUser(userId) }, [userId])

// ✅ 함수는 useCallback으로 고정
const handle = useCallback(() => doSomething(userId), [userId])
useEffect(() => { handle() }, [handle])

3. 함정

  • 의존성 배열을 아예 비우는([]) 회피는 stale closure로 이어진다 — 근본 해결 아님
  • ESLint react-hooks/exhaustive-deps 경고를 무시하지 말 것. 끄지 말고 원인을 고쳐라
  • setState 함수형 업데이트(setX(x => x+1))를 쓰면 의존성에서 x를 뺄 수 있다

자주 묻는 질문

의존성 배열을 비우면 무한 루프가 멈추는데 그래도 되나요?

증상은 사라지지만 이펙트가 최초 1회만 실행되고 이후 값 변화를 반영하지 못합니다(stale closure). 무한 루프의 진짜 원인은 "새 참조가 의존성에 들어간 것"이므로 그 참조를 useMemo/useCallback으로 고정하는 게 정답입니다.

객체를 꼭 의존성에 넣어야 하면요?

객체 전체 대신 실제로 쓰는 필드(obj.id 같은 원시값)만 의존성에 넣거나, 객체를 useMemo로 감싸 참조를 안정화하세요.

useEffect 대신 쓸 방법은 없나요?

렌더 중 계산으로 끝나는 값이면 useEffect가 아니라 그냥 렌더 중 파생값으로 계산하세요. 이펙트는 "외부 시스템 동기화"에만 쓰는 게 원칙입니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록