useEFFect 무한 루프가 발생하는 이유는?
주로 useEfffec에서 state 값을 변경할 때 일어난다.
import { useEffect, useState } from "react"; function App() { const [count, setCount] = useState(0); useEffect(() => { setCount(count + 1); }); return <div>{count}</div>; } export default App;
아래와 같은 에러를 띄우며 무한 루프가 발생하게 된다.

무한루프가 발생하는 이유는
- 첫 렌더링에서 count 값이 확인되고 useEffect 함수가 실행된다.
- useEffect 함수에서 setCount 메소드를 호출하고 count의 값을 업데이트한다.
- 바뀐 count 값으로 화면이 다시 렌더링 된다.
- 화면이 재렌더링 되면서 useEffect가 실행되고 setCount 를 호출하고…
이 과정이 반복되면서 무한루프에 빠지는 것이다.
무한루프 해결하기!
useEffect는 두 번째 인자로 종속성 배열을 받는다. useEffect는 리액트의 생명주기 함수의 역할을 하는데 두 번째 인자를 설정함으로써
componentDidUpdate
의 역할을 할 수 있다.만약 빈배열을 갖는 경우 처음 렌더링 될 때만 실행되고,
특정 값이 있는 배열인 경우, 처음 렌더링 될 때와 그 배열의 값이 변경될 때 마다 실행된다.
하지만 이러한 종속성 배열이 없다면 useEffect는
- 컴포넌트 생성 후 처음 렌더링 될 때,
- 컴포넌트에 새로운 props가 전달되어 렌더링 될 때,
- 컴포넌트의 state가 바뀌며 렌더링 될 때
작동하게 된다.
따라서 위에서의 무한루프 문제는 종속성 배열을 설정하여 해결할 수 있다.
import { useEffect, useState } from "react"; function App() { const [count, setCount] = useState(0); useEffect(() => { setCount(count + 1); }, []); return <div>{count}</div>; } export default App;
무한 루프가 발생하는 또 다른 이유
이 외에도 무한 루프가 발생하는 원인이 있는데 함수, 배열, 객체를 종속성 배열로 사용할 때이다.
useEffect는 얕은 비교를 하기 때문에 렌더링 될 때마다 참조 값이 변경되어 매번 호출되게 된다.
- 함수를 종속성으로 사용할 경우
function App() { const [count, setCount] = useState(0); function logResult() { return 2 + 2; } useEffect(() => { setCount((count) => count + 1); }, [logResult]); return ( <div className="App"> <p> value of count: {count} </p> </div>); }
- 함수는 컴포넌트가 리렌더링 될 때마다 새로 만들어진다.
- useEfffect가 실행되면서 count 값이 수정된다.
- 수정된 count 값으로 리렌더링된다.
- 함수가 새로 만들어진다.
…
이 문제를 해결하기 위해서 함수를 새로 만들지 않고 기억해서 재사용할 수 있는
useCallback
을 사용하면 된다.function App() { ... const logResult = useCallback(() => { return 2 + 2; }, []); useEffect(()=> { setCount((count)=> count+1); },[logResult]); ... }
- 배열을 종속성으로 사용하는 경우
const [count, setCount] = useState(0); const myArray = ["one", "two", "three"]; useEffect(() => { setCount((count) => count + 1); }, [myArray]);
→ useEffect는 얕은 비교를 사용하므로 리렌더링 될 때마다 배열에 대한 참조가 변경되어 무한루프에 빠진다.
이 문제는
useRef
를 이용하여 해결 할 수 있다. useRef는 값이 바뀐다고 해서 컴포넌트가 리렌더링되지 않기 때문에 안정적으로 종속성 배열로 사용할 수 있다!- 객체를 종속성으로 사용하는 경우
const [count, setCount] = useState(0); const person = { name: "Rue", age: 17 }; useEffect(() => { setCount((count) => count + 1); }, [person]); return ( <div className="App"> <p> Value of {count} </p> </div>);
→ 객체 역시 배열과 마찬가지로 얕은 비교로 인해 참조값이 변경되므로 무한 루프에 빠진다.
이 문제는
useMemo
를 사용해서 해결 할 수 있다. useMemo는 두 번째 파라미터로 넣는 종속성 배열의 내용이 바뀌면 첫 번째 파라미터로 등록한 함수를 호출해서 값을 연산한다. const person = useMemo( () => ({ name: "Rue", age: 17 }), [] //디펜던시가 없으므로 값이 변경되지 않는다. 즉, useEffect에서 person객체를 디펜던시로 사용해도 참조값이 변경되지 않기 때문에 유지가 된다는 뜻이다. ); useEffect(() => { setCount((count) => count + 1); }, [person]);
이렇게 빈 배열를 설정할 경우 디펜던시가 없기 때문에 참조값이 변경되지않고 이전의 값을 재사용하기 때문에 참조값이 그대로 유지된다.