[프리온보딩] useEffect

useEffect

React에서 의존성 배열을 이해하는 건 정말 중요하다. 잘 몰라서 의존성 배열을 제대로 사용하지 못하면, React 앱에서 버그가 자주 발생할 수 있다. 그래서 이번에는 useEffect의 의존성 배열을 중심으로, 왜 이게 중요한지 그리고 어떻게 잘 사용하는지 정리해 보겠다.

 

 

 의존성 배열이란?

의존성 배열은 useEffect의 두 번째 인자로 들어가는 배열이다. 이 배열을 설정하지 않으면, Effect는 컴포넌트가 리렌더링 될 때마다 실행된다. 반면, 빈 배열을 넘기면 컴포넌트가 첫 번째로 렌더링될 때만 실행된다.

useEffect의 구조는 이렇다.

useEffect(effect, 의존성)

여기서 effect는 함수로, 의존성은 배열 형태로 표현된다. 

의존성은 한 요소가 제대로 작동하기 위해 다른 요소들을 필요로 할 때 그 요소들에 '의존한다'라고 표현한다. 예를 들어, 'B, C, D는 A의 의존성이다'라고 말할 수 있다.

 


useEffect의 의존성 배열은, effect 함수가 사용하는 외부 값들이다. 예를 들어 보자.

function Component(){
	const [count, setCount] = useState(0);
	
	const effect = () => {
		document.title = `you clicked ${count} times`;
	};

	useEffect(effect, [count]];
}

여기서 effect 함수는 외부의 'count' 값을 사용한다. 

따라서 effect 함수의 의존성은 'count'가 되고, 이를 의존성 배열에 넣어줘야 한다. 

이렇게 설정하면 useEffect는 리렌더링 후 의존성 배열을 검사해, 배열 안의 값이 변경되었을 때 새로운 의존성으로 effect를 다시 실행한다.

 

 


useEffect 의존성 배열의 잘못된 활용

useEffect의 의존성 배열은 React에서 중요한 부분이지만 의존성 배열을 잘못 사용하는 경우가 종종 있는데, 특히 필요한 의존성을 배열에 넣지 않는 실수가 많다.

예를 들어보자.

function Component(){
	const [count, setCount] = useState(0);

	useEffect(() => {
		document.title = `you clicked ${count} times`
	}, []);
}

이 코드의 문제는 무엇일까? 

여기서 count가 변경될 때 document.title도 변경되어야 하는데, 그렇게 되지 않는다. 

이유는 의존성 배열에 count를 넣지 않았기 때문이다. 의존성 배열이 비어 있으면, useEffect는 컴포넌트가 첫 번째로 렌더링 될 때만 실행되고, 그 후에는 count 값의 변화에 반응하지 않는다.

이처럼 useEffect의 의존성 배열에 필요한 의존성을 제대로 넣지 않으면, 코드가 예상대로 동작하지 않을 수 있다는 걸 알아야 한다.

 

https://codesandbox.io/p/sandbox/useeffect-wrong-dependency-d0k9en

 

codesandbox.io



 useEffect 의존성 배열을 잘 설정하는 법

 

useEffect에서 버그 없이 의존성 배열을 잘 설정하기 위해 기본적으로 따라야 할 원칙 두 가지가 있다.

 

1. 가능하면 의존성을 적게 만들기

// 잘못된 예시

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalID = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => clearInterval(intervalID);
  }, [count, setCount]);

  return (
    <div>
      <h1>count:{count}</h1>
    </div>
  );
}
// 올바른 예시

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalID = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    return () => clearInterval(intervalID);
  }, [setCount]);

  return (
    <div>
      <h1>count:{count}</h1>
    </div>
  );
}
 

useState – React

The library for web and native user interfaces

react.dev

 

2. 모든 의존성을 의존성 배열에 명시하기

// 잘못된 예시

function Component(){
	const [count, setCount] = useState(0);

	useEffect(() => {
		document.title = `you clicked ${count} times`
	}, []);
}
// 올바른 예시

function Component(){
	const [count, setCount] = useState(0);

	useEffect(() => {
		document.title = `you clicked ${count} times`
	}, [count]);
}

일반 변수, state, props 같은 경우는 비교적 쉽게 의존성 배열에 넣을 수 있다.

하지만 함수나 객체 같은 참조형 데이터는 매번 새롭게 생성되어 비교 시 다른 객체로 인식되어 문제가 될 수 있다.

// 때문에 이 코드는 무한 루프를 반복하게 됨

function Component(){
	const [count, setCount] = useState(0);

	const increaseCount = () => {
		setCount(prev => prev + 1);
	}

	useEffect(increaseCount, [increaseCount]];
}

 

이를 해결하기 위해 함수를 effect 안에 선언하거나 컴포넌트 바깥으로 이동시키는 방법, 그리고 메모이제이션을 사용하는 방법이 있다.

 

의존성 제거하기 >> 함수를 effect 안에 선언하기

function Component(){
	const [count, setCount] = useState(0);

	useEffect(() => {
		const increaseCount = () => {
			setCount(prev => prev + 1);
		};

		increaseCount();
	}, []];
}

 

함수를 컴포넌트 바깥으로 이동시키기

// 잘못된 예시
function Component() {
	const getUserAuth = () => {
		localStorage.getItem("ACCESS_TOKEN");
	};

	useEffect(() => {
		const token = getUserAuth();
		// login....
	}, []];
};
// 올바른 예시 
function Component() {

	useEffect(() => {
		const token = getUserAuth();
		// login....
	}, [getUserAuth]];

};

const getUserAuth = () => {
	localStorage.getItem("ACCESS_TOKEN");
};

 

메모이제이션

function Component(){
	const [count, setCount] = useState(0);

	const increaseCount = () => {
		setCount(prev => prev + 1);
	}

	useEffect(() => {
		// do something 1
		increaseCount();
	}, []];

	useEffect(() => {
		// do something 2
		increaseCount();
	}, []];
}
function Component(){
	const [count, setCount] = useState(0);

	const increaseCount = useCallback(() => {
		setCount(prev => prev + 1);
	}, []);

	useEffect(() => {
		// do something 1
		increaseCount();
	}, [increaseCount]];

	useEffect(() => {
		// do something 2
		increaseCount();
	}, [increaseCount]];
}

 

 

참고자료

 

A Complete Guide to useEffect — overreacted

You wrote a few components with Hooks. Maybe even a small app. You’re mostly satisfied. You’re comfortable with the API and picked up a few tricks along the way. You even made some custom Hooks to extract repetitive logic (300 lines gone!) and showed i

overreacted.io