Lighthouse of FE biginner

[클린코드 리액트] useEffect 바르게 사용하기 본문

[WEB] 프론트엔드

[클린코드 리액트] useEffect 바르게 사용하기

[FE] Lighthouse 2024. 6. 10. 21:17

useEffect 훅은 리액트에서 부수 효과를 일으키는 훅으로 여러개의 컴포넌트 생명주기를 다룰수 있는 훅 입니다. 그만큼 많이 사용하고 자주 사용하는 훅이지만 잘못 사용하면 무한 루프가 발생하던지 예상치 못한 버그가 발생할 수 있습니다.

 

가끔 개발된 컴포넌트를 보다보면 하나의 useEffect 훅에 여러 역할을 하는 함수를 넣는 경우가 있습니다. 이는 정말 좋지 못한 케이스이며 부수 효과로 인해 불필요한 로직이 실행됩니다.

 

function App() {
  const [isSuccess, setIsSuccess] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetch = async () => {
      const res = fetch("url")
        .then((res) => {
          setIsError(false);
          setIsSuccess(true);
          return res.json();
        })
        .catch(() => {
          setIsError(true);
          setIsSuccess(false);
        });
      return res;
    };
    
    fetch();

    if (isSuccess) {
      // ~~~
    }

    if (isError) {
      // ~~
    }
  }, [isError, isSuccess]);

  return (
    ...
  );
}

 

위 코드는 극단적 예시입니다. 사실 실제로는 이런식으로 코드를 작성하는 케이스는 없을 것 입니다. 위 예시 코드에서는 하나의 useEffect 함수에서 데이터 패칭과 성공, 실패 케이스에 따른 분기 처리까지 함께 처리하고 있습니다. 위 케이스의 경우 promise가 resolve되기 이전에 성공, 실패 분기 처리를 함으로써 결과값을 예측하기 어려워집니다. 또한 의존성 배열에 promise가 resolve될 때 변경해주는 플래그 값을 넣어줬기 때문에 무한루프가 발생할 위험이 있습니다.

 

이 경우 해당 훅의 함수를 아래와 같이 분리할 수 있습니다.

function App() {
  const [isSuccess, setIsSuccess] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetch = async () => {
      const res = fetch("url")
        .then((res) => {
          setIsError(false);
          setIsSuccess(true);
          return res.json();
        })
        .catch(() => {
          setIsError(true);
          setIsSuccess(false);
        });
      return res;
    };

    fetch();
  }, []);

  useEffect(() => {
    if (isSuccess) {
      // ~~~
    }

    if (isError) {
      // ~~
    }
  }, [isSuccess, isError]);

  return (
    ...
  );
}

 

useEffect 훅의 로직을 분리해 각 함수가 한 가지의 역할만 하도록 분리했습니다. 이럴 경우 코드의 가독성이 좋아지고 SOLID 원칙 중 SRP(단일 책임의 원칙)을 지킬 수 있게 됩니다. 또한 데이터를 패칭하는 훅에서 불필요한 의존성을 제거함을써 불필요한 부수효과를 방지할 수 있게됩니다.

 

만약 useEffect 함수의 역할을 코드상 눈에 띄게 드러내기 어렵다면 기명 함수를 활용할 수 있습니다.

  useEffect(function getData() {
    const fetch = async () => {
      const res = fetch("url")
        .then((res) => {
          setIsError(false);
          setIsSuccess(true);
          return res.json();
        })
        .catch(() => {
          setIsError(true);
          setIsSuccess(false);
        });
      return res;
    };

    fetch();
  }, []);

 

useEffect 훅 내부 함수에 화살표 함수를 넣어주는 것이 아닌 표현식을 활용한 함수를 넣어줬습니다. 이렇게 간단한 방법으로 해당 훅의 역할을 명확하게 표현할 수 있습니다.