리액트 useEffect cleanUp

myung hun kang·2023년 1월 23일
0

최근 FUN편log 프로젝트 리펙토링 작업을 진행했다.

이전 포스팅에서 Intersection observer를 이용하여 무한 스크롤을 구현하는 것을 시작으로 리펙토링을 진행했는데

그 이후 코드에 큰 변화는 없었지만 리펙토링을 마무리하기까지 꽤나 시간이 걸렸다.

이유는 ... 아주 사소한 부분 때문이었는데

useEffect

때문이다.

  • The Effect Hook lets you perform side effects in function components
  • useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

reactjs.org을 보면 위의 문장을 볼 수 있다.

react 프로젝트 내에서 side effects들을 다뤄야 할 때 사용하게 된다고 하는데
처음 배울 때는 개념에 대해서 좀 더 생각하고 조심히 사용했던거 같은데

지난 프로젝트를 하면서는

요기에 effect가 들어가야겠네~

하는 느낌으로 구현했던거 같다. ㅠㅠ

그리고 다시 리펙토링하면서 useEffect의 clean Up 에 대해서 고민하게 되었다.

useEffect CleanUp

clean Up 함수가 필요할까?

필요한 경우가 있고, 아닌 경우가 있다.

useEffect error

뭐 이런 에러가 뜬다면야 당연히 해야하지만 ...

useEffect는 effect의 dependency 에 있는 value가 바뀔때마다 re-render 되는 특징이 있다.

또 react v18에서 strict 모드를 켠다면 18버전의 새로운 앱 검사기능 때문에 앱이 실행되는 처음에 모든 컴포넌트가 unmount 되었다가 다시 mount되는 과정을 겪게 된다.

react 공식문서 글

여하튼 이러한 re-render이 개발자가 뜻하지 않는 행동을 하거나 메모리 누출을 일으킬 수 있다.

여기까지의 얘기로는 모든 상황에 필요해보이지만 그렇지는 않다.

clean up 필요없는 경우

공식문서에 따르면

Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that don’t require a cleanup.

즉 네트워크 요청, DOM요소 변경 그리고 로깅 상황에서 사용이 된다고 한다.

예를 들면 다음과 같이 유저가 로그인을 한 경우 로그인 한 사용자의 상태를 localStorage에 저장했다면 다음과 같이 사용자가 있는 경우만 loggedIn state가 true가 되도록 할 수 있을 것이다.

  useEffect(() => {
    const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");

    if (storedUserLoggedInInformation === "1") {
      setIsLoggedIn(true);
    }
  }, []);

여기서는 dependency에 따로 값을 주지않아 컴포넌트 내에서 딱 한번만 실행되도록 한 경우이다.

clean up 필요한 경우

그렇다면 필요한 경우는 뭐라고 되어 있을까

we might want to set up a subscription to some external data source.

외부에서 가져온 데이터를 구독하고 싶을 때! 사용한다고 되어있다.

간단히 생각하면 외부에서 data를 fetch해서 render 하고 싶을 때 useEffect를 사용하면 clean up이 필요하다는 것이다.

useEffect 내에서 비동기 http 통신이나 setTimeout과 같은 비동기 함수를 호출하는 경우

clean up을 하지 않으면 비동기 통신이 resolve 되기전에 컴포넌트가 unmount 되면 앞서 본

useEffect error

에러가 뜨게 되는 것이다.

그래서 clean up으로 비동기 함수를 다시 호출하면 useEffect에 등록한 구독을 취소하게 된다.

  useEffect(() => {
    const identifier = setTimeout(() => {
      console.log("Checking form validity!");
      setFormIsValid(emailIsValid && passwordIsValid);
    }, 1000);

    return () => {
      console.log("CLEANUP");
      clearTimeout(identifier);
    };
  }, [emailIsValid, passwordIsValid]);

위처럼 setTimeout으로 간단한 login form validation을 만든다면

clean up 함수를 통해 매 순간마다 validate 하지 않고 1초라는 시간 간격으로 validate 하게 만들 수 있는 것이다.

위 clean up 부분은

    return () => {
      clearTimeout(identifier);
    };

맨 처음 컴포넌트가 렌더링 될 때 말고는 컴포넌트가 unmount된 후 뿐만아니라 dependency에 의해 useEffect 내 함수가 execute 되기 전에 작동하게 된다.

이러한 작업이 이루어져서 메모리 누출을 막아주는 것인데

fetch를 쓰거나 axios를 쓰거나 할 때 어떻게 clean up function을 만들지에 대한 use case는 googling으로 찾아볼 수 있다.

참고 링크


실사용..?

위 내용을 복기하고 프로젝트에 문제를 보니 위에서 언급한 경우에 딱 부합하지는 않아보이지만 여튼 clean up을 사용해서 해결할 수 있는 곳이 보이게 되었다.


  useEffect(() => {
    if (mapApi instanceof kakao.maps.Map) {
      if (sortedConv) {
        sortedConv.forEach((list) => {
          setMarkers(list, mapApi)
        })
      }
      setMyMarker(mapApi)
    }
    return () => {
      if (mapApi instanceof kakao.maps.Map) setMyMarker(mapApi)
    }
  }, [sortedConv, mapApi, setMarkers, setMyMarker])

위 함수는 서버와의 통신으로 받아온 data list로 지도에 그릴 마커를 만들고 저장해두는 effect 이다.

여기서 setMyMarker 로 내 현위치 마커를 만들어주는데

여러 값들에 얽혀있는 effect라 찾기힘든 side effect로 인해 다른 페이지에서 다시 원래 페이지로 넘어올 때 마커가 사라지는 버그가 있었다.

위처럼 clean up 함수를 설정하여 effect 가 실행되고 clean up을 해주며 마커를 마지막에 만드는 것으로 오류를 해결했다.

위 effect와 관련된 부분을 모두 건들이면 이처럼 해결하지 않아도 되는 부분이라 생각되기도 한다.

하지만 팀원이 짠 코드와 나의 코드가 섞여있어서 모든 프로젝트를 마무리한 지금으로서는 건들지 쉽지않는 부분이 있다. ^^;;

협업하면서 파일 구조나 코드 구조를 어떻게 짜야할지 어떻게 코드를 추상화 해야할 지를 더 생각하게 만들었다.
추후에 해당 내용에 대해서 어느 정도 정리가 되면 옮겨보리라...

여튼 오류는 해결하고 리펙토링을 마무리했다.

useEffect clean up 에서 프로젝트 트러블 슈팅으로 글이 변질되기는 했는데

useEffect와 같이 자주 사용하게되는 hook에 대해서 다시한번 복습하고 공부하게 된 계기였다.

아 최근에 react query를 사용하면서 caching으로 비동기 data 상태를 다루는 것을 알게 되었다.
위 useEffect로 비동기를 다루는 것보다 react query가 훨씬 좋다는 느낌을 받았다.

어여 공부해서 갈아타야지 😙

profile
프론트엔드 개발자입니다.

0개의 댓글