2026. 6. 30.

React useEffect는 왜 계속 실행될까? 상태 업데이트와 의존성을 함께 본다

React useEffect가 계속 실행될 때 상태 업데이트와 의존성 배열을 대조하고, 객체·함수·Strict Mode를 구분해 반복 원인을 좁히는 순서입니다.

4 min read
React useEffect는 왜 계속 실행될까? 상태 업데이트와 의존성을 함께 본다 대표 이미지

AI가 React 컴포넌트를 고쳐 줬는데 화면이 멈추거나, API 요청이 계속 찍히거나, 콘솔 로그가 끝없이 반복되면 초보자는 보통 파일 전체를 다시 붙여 넣는다. 하지만 useEffect 무한 반복은 대부분 파일 전체 문제가 아니다. 먼저 볼 곳은 Effect 안에서 상태를 바꾸는 줄그 상태 때문에 바뀌는 의존성 값이다.

useEffect 문서 - React는 무한 사이클이 생기려면 Effect가 상태를 업데이트하고, 그 업데이트로 다시 렌더링되어 의존성이 바뀌는 두 조건이 함께 있어야 한다고 설명한다. 목표는 React를 전부 이해하는 것이 아니라 반복을 만드는 두 줄을 찾는 것이다.

React useEffect가 렌더, Effect, 상태 변경, 의존성 변화로 다시 실행되는 흐름

두 번 실행과 무한 반복을 구분하고 두 줄을 대조한다

개발 모드에서 로그가 정확히 두 번 보였다고 무한 루프라고 단정하면 안 된다. StrictMode 문서 - React에 따르면 Strict Mode는 Effect의 setup과 cleanup을 개발 중 한 번 더 실행해 정리 로직을 검사한다. 반면 로그나 요청 횟수가 계속 늘고 상태값과 의존성이 함께 바뀐다면 무한 반복 경로를 의심할 수 있다.

useEffect 마지막에 있는 [] 또는 [userId] 같은 배열이 의존성 배열이다. 이 배열에 들어간 값이 이전 렌더와 달라지면 Effect가 다시 실행된다. React는 각 의존성을 Object.is로 비교한다. 첫 확인은 배열 안 값이 매번 바뀌는지다.

useEffect(() => {
  loadUser(userId);
}, [userId]);

이 코드는 userId가 바뀔 때 다시 실행된다. 여기까지는 정상이다. 문제는 Effect 안에서 바꾼 상태가 다시 의존성에 들어갈 때 생긴다.

useEffect(() => {
  setCount(count + 1);
}, [count]);

count가 바뀌면 Effect가 실행되고, Effect가 다시 count를 바꾼다. 이 구조가 보이면 파일 전체를 고치라고 하지 말고 이 상태 변경이 Effect 안에 있어야 하는지부터 묻는다.

Effect 안에서 setState, setUsers, setCount, setData처럼 상태를 바꾸는 함수를 찾는다. 그 다음 같은 상태값이 의존성 배열에 있는지 본다.

useEffect(() => {
  setUsers([...users, newUser]);
}, [users, newUser]);

이 코드는 users를 읽고 다시 users를 바꾼다. 상황에 따라 맞는 코드일 수도 있지만 화면이 계속 렌더링된다면 먼저 의심해야 한다. 특히 AI가 "최신 값을 반영해야 한다"는 이유로 의존성을 무조건 추가했다면 반복 조건이 생겼을 수 있다.

exhaustive-deps 문서 - React는 Effect 안에서 쓰는 반응형 값을 의존성 배열에 포함해야 하며, 린터를 속이려 한다면 구조를 바꿔야 할 가능성이 크다고 안내한다. 그러니 의존성 삭제는 마지막 선택지로 둔다.

객체와 함수가 매번 새로 만들어지는지 본다

상태를 직접 바꾸지 않아도 객체나 함수가 매 렌더마다 새로 만들어지면 Effect가 계속 실행될 수 있다. 초보자가 자주 놓치는 형태는 아래와 같다.

const options = { page, keyword };

useEffect(() => {
  search(options);
}, [options]);

컴포넌트가 다시 렌더링될 때마다 { page, keyword } 객체는 새로 만들어진다. 값이 같아 보여도 참조가 달라지면 의존성이 바뀐 것으로 볼 수 있다. 함수도 비슷하다.

const load = () => fetchUser(userId);

useEffect(() => {
  load();
}, [load]);

이 경우 AI에게 useMemouseCallback을 바로 붙여 달라고 요구하지 않는다. 먼저 객체를 Effect 안으로 옮길 수 있는지, 또는 필요한 원시값만 의존성에 둘 수 있는지 확인한다.

useEffect(() => {
  const options = { page, keyword };
  search(options);
}, [page, keyword]);

이렇게 바꾸면 의존성이 새 객체 options가 아니라 실제로 변해야 하는 page, keyword로 좁아진다. 렌더가 느린 문제까지 함께 보인다면 React 렌더 원인을 먼저 좁히는 점검법으로 넘어가면 된다.

콘솔에서 달라지는 값 하나를 찾는다

무한 반복이 의심될 때 console.log("실행됨")만 찍으면 원인을 못 찾는다. 몇 번 실행됐는지는 보이지만 어떤 값이 바뀌었는지 보이지 않는다. 로그는 의존성 배열에 있는 값과 상태 변경 직전 값을 같이 찍어야 한다.

useEffect(() => {
  console.log("effect", { count, userId });
  setCount(count + 1);
}, [count, userId]);

로그에서 count만 계속 올라가면 상태 변경이 반복을 만들고 있을 가능성이 크다. userId는 그대로인데 Effect가 계속 실행된다면 다른 의존성, 객체, 함수가 새로 만들어지는지도 본다. 목표는 로그를 많이 찍는 것이 아니라 반복마다 달라지는 값 하나를 찾는 것이다. 상태 객체를 직접 바꾼 흔적이 있다면 React state 직접 수정과 화면 업데이트의 관계도 함께 확인한다.

React useEffect 무한 반복을 의존성, 상태 변경, 객체와 함수, 로그, 최소 수정으로 확인하는 체크리스트

AI에는 Effect 하나만 고치게 하고 다시 실행한다

반복 원인을 좁힌 뒤에는 AI에게 파일 전체 수정을 맡기지 않는다. 아래 프롬프트처럼 범위를 고정한다.

React useEffect 무한 반복이 의심됩니다.
반복이 생기는 후보는 아래 코드의 useEffect 하나입니다.

[문제 useEffect 코드 붙여넣기]

1. Effect 안에서 상태를 바꾸는 줄을 표시해 주세요.
2. 그 상태 또는 매번 새로 만들어지는 객체, 함수가 의존성 배열에 있는지 확인해 주세요.
3. 의존성을 무조건 삭제하지 말고, 반복을 멈추는 최소 수정 후보를 2개만 제안해 주세요.
4. 각 후보가 왜 안전한지와 다시 확인할 콘솔 로그 한 줄을 같이 주세요.

핵심은 AI가 고칠 범위를 useEffect 하나로 좁히는 것이다. 수정 뒤에는 로그나 네트워크 요청이 계속 증가하지 않는지, 화면에 필요한 결과가 남는지 다시 확인한다.

확인 순서는 다섯 줄이면 충분하다.

1. 개발 Strict Mode의 한 번 추가 실행인지 구분한다.
2. Effect 안의 상태 업데이트를 찾는다.
3. 그 상태 때문에 의존성이 다시 바뀌는지 본다.
4. 객체와 함수 의존성이 매번 새로 만들어지는지 본다.
5. AI에게 useEffect 하나만 놓고 최소 수정 후보를 묻는다.

반복을 멈추는 첫 행동은 의존성 삭제가 아니라 반복 조건 확인이다.

참고 출처

이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.

다음으로 읽을 기사

같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.

댓글 0

이 글을 읽은 독자들의 생각을 나눠보세요.

비밀번호(선택)

첫 번째 댓글을 남겨보세요

여러분의 생각이 다른 독자에게 도움이 됩니다.