useEffect

강혁준·2024년 10월 31일

프론트엔드

목록 보기
3/6
post-thumbnail

Side Effect?

함수가 어떤 동작을 할 때 입력으로 인한 출력 외의 다른 값을 조작한다면 이 함수에는 Side Effect가 있다고 표현한다. 예를 들면 Side Effect에는 데이터 가져오기, 구독 설정하기, 수동으로 React의 DOM을 조작하기 등이 있다.

React의 경우, 이러한 Side Effect가 컴포넌트 내부에 존재한다면 stateprops의 변화로 인해 리렌더링 될 때마다 로직이 실행될 수 있다. 그렇기 때문에, React에서는 이런 Side Effect를 일으키기 위한 적절한 장소인 useEffect 훅을 제공한다.

React 컴포넌트에는 일반적으로 정리(clean-up)가 필요한 것과 그렇지 않은 것이 있다.

useEffect는 어떤 일을 할까?

useEffect훅을 사용하여 우리는 React 컴포넌트가 렌더링 이후에 어떤 동작을 수행해야 하는지 명시한다. React는 우리가 useEffect에 넘긴 함수(effect)를 기억했다가, DOM 업데이트를 수행한 이후에 불러낼 것이다.

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

위 예시의 경우에는 effect를 통해 문서의 타이틀을 조작하지만, 이 외에도 데이터를 가져오는 등의 다른 Side Effect 역시 수행할 수 있다.

useEffect는 기본적으로 첫번째 렌더링과 이후 모든 리렌더링에서 수행되지만, deps 배열을 통해서 이를 필요에 맞게 변경할 수 있다. effect가 수행되는 시점을 마운팅과 리렌더링이라는 방식으로 생각하는 대신, effect를 렌더링 이후에 발생하는 것으로 생각하는 것이 더 쉽다. React는 effect가 수행되는 시점에 이미 DOM이 업데이트 되었음을 보장한다.

useEffect에 대한 좀 더 자세한 설명

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });
}

count라는 state 변수를 선언한 뒤, useEffect 훅에 effect 함수를 전달하였다. 이 effect 함수에서는 브라우저 API를 사용해 문서 타이틀을 지정한다.

잘 생각해보면, useEffect에 전달되는 effect는 모든 렌더링마다 달라지는 것을 알 수 있다. 이는 의도된 것인데, count 값이 제대로 업데이트 되는 지에 대한 걱정 없이 effect 내부에서 그 값을 읽을 수 있게 하는 부분이기도 하다.

이 부분에 대해서는 클로저에 대한 이해가 필요하다. count가 변화해서 리렌더링이 일어날 때마다 새로운 effect 함수가 선언되는데, 이 effect 함수는 선언된 환경의 특징을 기억하기 때문에 최신 count 값을 포함하는 환경에서 선언된다. 덕분에 effect 함수 내부에서는 항상 최신의 count 값을 참조할 수 있게 된다.

effect 함수를 익명 함수로 전달하는 방식을 권장하는 이유 역시 이 때문인데, effect를 익명 함수로 전달해야 매 렌더링마다 최신의 stateprops를 반영한 클로저로 생성될 수 있기 때문이다.

Clean up Effect

Clean up Effect는 이전에 일으킨 Side Effect를 정리할 필요가 있을 때 사용한다. 예를 들면 컴포넌트가 mount 되었을 때 이벤트 리스너를 등록했다고 가정해보자.

useEffect(() => {
	const handleResize = () => {
		console.log("resized");
	}
	window.addEventListener("resize", handleResize);
}, [])

만약 이 컴포넌트를 더이상 페이지에서 사용하지 않는다면, useEffect에서 등록한 이벤트 리스너는 더이상 필요가 없다. 만약 이벤트 리스너를 제거하지 않는다면 이 컴포넌트가 mount 될 때마다 이벤트 리스너가 새롭게 등록되므로, 메모리 누수가 발생할 수 있다.

따라서 아래와 같이, useEffectreturn 문에서 Side Effect를 정리하는 코드를 작성하면 된다.

useEffect(() => {
	const handleResize = () => {
		console.log("resized");
	}
	window.addEventListener("resize", handleResize);
	
	return () => window.removeEventListener("resize", handleResize);
}, [])

참고

Using the Effect Hook – React

[React] useEffect란?

profile
안녕하세요 프론트엔드 개발자가 되고 싶은 강혁준 입니다.

0개의 댓글