useEffect는 가장 먼저 실행되지 않는다.

Duboo·2023년 10월 23일
0

REACT HOOK

목록 보기
11/16
post-thumbnail

훅을 사용하다보면 useEffect을 자주 사용하게 된다.

이때 useEffect의 실행 시점에 대해서 제대로 알고 넘어가지 않으면 예상치 못하게 코드가 동작할 수 있기에 정리하고 넘어가려한다.


가장 많이 하는 실수

useEffect가 컴포넌트 안에서 가장 먼저 실행된다고 착각하기 쉽다.

const App = () => {
  console.log("App 컴포넌트 렌더링");

  useEffect(() => {
    console.log("useEffect 실행");
  }, []);

  return <div>{console.log("컴포넌트 내부 실행")}</div>;
};
출력 순서
1App 컴포넌트 렌더링
2컴포넌트 내부 실행
3useEffect 실행

컴포넌트의 실행 순서는 위의 결과처럼 useEffect가 가장 마지막에 출력되는걸 확인할 수 있다.


useEffect의 실행 시점

흔히 알고 있는 실행 시점은 mount, update, unmount 되는 3가지 경우인데,

사실 글만보고 위의 3가지 경우가 정확하게 언제 일어나는지 생각하기가 어렵기 때문에 공식 문서에서 안내하는 class의 3가지 기능과 함께 살펴보면

React 컴포넌트 안에서 데이터를 가져오거나 구독하고, DOM을 직접 조작하는 작업을 이전에도 종종 해보셨을 것입니다.

우리는 이런 모든 동작을 “side effects”(또는 짧게 “effects”)라고 합니다. 왜냐하면 이것은 다른 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이기 때문입니다.

Effect Hook, 즉 useEffect는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 해줍니다.

React class의 componentDidMountcomponentDidUpdate, componentWillUnmount와 같은 목적으로 제공되지만, 하나의 API로 통합된 것입니다. | 출처: 리액트 공식문서

위 3가지의 기능을 합쳐놓은 훅이라고 생각하면 된다.

기본적인 useEffect의 실행은 마운트 이후에 이루어지고, 상태(state)의 변화로 리렌더링 이후, 언마운트 이전에 이루어진다.

mount | 컴포넌트가 생성되고 DOM에 추가되는 단계

componentDidMount: 컴포넌트가 DOM에 추가된 후 호출됨
즉, 컴포넌트가 처음 렌더링된 후에 실행한다는 의미

컴포넌트 첫 렌더링 후

componentDidMount() {
	// 특정 작업 수행
}

위처럼 mount는 전체적인 첫 렌더링을 의미하기에 componentDidMount 내부 로직은 첫 렌더링 직후 실행하는걸 알 수 있다. (대표적으로 api 호출 등..)


실제로 그런가? 🤨

위 설명대로 라면 useEffect는 렌더링 이후 실행되기 때문에 렌더링이 일어나는 동시에 데이터를 화면상에 뿌려줄 수 없지만 우리가 보기에는 동시에 뿌려주는거 처럼 보인다.

이를 확인하기 위해 setTimeout을 사용해서 실제 데이터가 뿌려지기까지의 실행 순서를 확인해볼 필요가 있다.

const App = () => {
  const [serverData, setServerData] = useState("");
  console.log("App 컴포넌트 렌더링");

  useEffect(() => {
    setTimeout(() => {
      console.log("useEffect mount");
      setServerData("Hello");
    }, 3000);
  }, []);

  return (
    <div>
      {console.log("컴포넌트 리턴")}
      <h1>{serverData}</h1>
    </div>
  );
};

출력 순서
1App 컴포넌트 렌더링
2컴포넌트 리턴
useEffect 실행...
3useEffect mount
4App 컴포넌트 렌더링
5컴포넌트 리턴

딜레이를 주고 마운트 이후에 useEffect가 실행된다는걸 확인할 수 있다.


useEffect의 의존성배열

의존성 배열(Dependency Array)은 useEffect, useCallback, useMemo 등의 Hook에서 사용되는 배열로, Hook이 불필요하게 반복해서 실행되는 것을 방지하여 성능을 최적화하기 위해서 사용한다.

의존성 배열의 주요 목적

  • Hook의 재실행 조건 설정: 의존성 배열에 포함된 값들이 변경될 때만 훅이 재실행되도록 조건을 설정한다.
    • 이를 통해 불필요한 실행을 방지할 수 있다.
  • 최신 상태 유지: 의존성 배열을 사용하면 Hook이 항상 최신 상태의 값을 참조할 수 있다.
    • 이를 통해 불필요한 연산을 방지할 수 있다.
  • 의존성 관리의 명확성: 의존성 배열을 사용하면 훅이 어떤 값에 의존하고 있는지 명확하게 파악할 수 있다.
    • 이는 코드의 가독성을 높이고, 의도하지 않은 의존성 관계를 방지할 수 있다.
  • componentDidUpdate: 컴포넌트가 업데이트된 후 호출
    • componentDidUpdate와 의존성배열은 비슷해보이지만 정확하게 동일하지는 않다.

정리

  1. 컴포넌트 렌더링 이후 useEffect 훅이 실행된다.

  2. useEffect 훅은 의존성 배열에 지정된 값들을 이전 렌더링과 비교한다.

  3. 의존성 배열에 지정된 값 중 하나라도 이전 렌더링과 다르다면, useEffect의 콜백 함수가 실행된다.

  4. useEffect의 콜백 함수는 부수 효과 작업을 수행한다. 예를 들어 API 호출, 이벤트 등록, 구독 등의 작업을 수행할 수 있다.

  5. 컴포넌트가 다음으로 렌더링될 때까지 useEffect의 콜백 함수는 실행되지 않는다.

  6. 컴포넌트가 다음으로 렌더링될 때, 다시 의존성 배열을 비교하고 변경 여부에 따라 useEffect의 콜백 함수를 실행할지 결정한다.

  7. 의존성 배열이 비어있는 경우 ([]), useEffect의 콜백 함수는 컴포넌트가 처음 렌더링될 때에만 실행되며, 컴포넌트가 언마운트될 때에만 정리(clean-up) 함수가 실행된다.

  8. 의존성 배열에 아무 값도 지정하지 않는 경우, 콜백 함수는 매 렌더링마다 실행되므로 주의해야 한다.

profile
둡둡

0개의 댓글