본문 바로가기
이슈

useEffect의존성에 ref사용해서 발생하는 문제 (feat. useCallback, useMemo)

by 케찹이 2024. 3. 14.

 

저번 프로젝트에 무한 스크롤을 구현하면서 마주친 문제가 있었다. 바로 useRef와 useEffect를 사용하면서 발생한 문제였다.

 

1. 발단

무한 스크롤 기능을 구현하기 위해서는 트리거가 되는 DOM을 가져와야 한다. 즉 바닐라JS에서 document.querySelector()와 같은 메소드로 무한 스크롤을 하게 하는 트리거가 필요했다. (예, 보여지는 영화리스트중 마지막 영화가 사용자 window화면에 보이면 무한 스크롤을 통해 다음 영화들을 로드하기 위해서 마지막 영화에 트리거를 놓는 것)

 

구글링을 통해서 useRef을 사용하면 JSX의 DOM을 가져올 수 있다고 하여 그것만 보고 과감하게 useRef()를 사용하였다. 

당시 작성했던 주요 코드를 보면 아래와 같음

  const imageRef = useRef(null);
  
  ........
  
  useEffect(() => {
    const observer = new IntersectionObserver(handleIntersection, {
      threshold: 0.5,
    });

    if (imageRef.current) {
      observer.observe(imageRef.current);
    }

    return () => {
      observer.disconnect();
    };
  }, [imageRef, page]);
  
  .........

 

일단 문제가 엄청 많은 코드이다. 그럼에도 불구하고 당장 useRef의 특징 때문에 생긴 문제들을 돌아보자면 useRef는 자신의 state가 바뀌어도 다시 렌더링하지 않는 특징이 있다. 이와 같은 특징 때문에 useEffect의 의존성 배열에 ref값을 할당하여도 useEffect는 해당값의 변화를 감지할 수가 없게된다. 그럼으로 useCallback()을 사용하여서 해당 문제를 해결할 수 있다.

 

2. 해결

  const observer = useMemo(
    () =>
      new IntersectionObserver(handleIntersection, {
        rootMargin: "300px",
      }),
    []
  );
  
  const imageRef = useCallback(
    (element) => {
      if (!element) return;
      observer.observe(element);
    },
    [observer]  // 생략 가능
  );
  
  ....
  return (
        <div ref={imageRef} />
  )

 

위 코드를 보면 JSX에서 ref를 통해 imageRef로 지정해주었고 해당 ref값은 const imageRef에서 useCallback()안에서 element으로 사용되었다. 이렇게 함으로서 무한 스크롤에 쓰이는 트리거를 지정해줄 수 있었음.

 

그리고 useCallback과 useMemo를 공부하면서 정말 간과했던 것은 객체타입의 변수들은 매번 렌더링시 그 state가 계속해서 변환이 된다는 것이고 이를 해결하기 위해서 우리는 useCallback과 useMemo를 통해서 컴포넌트 최적화를 해주어야 한다는 것이다.

 

맨 위의 코드의 useEffect안에서 IntersectionObserver를 useEffect안에서 의존성 배열의 state들이 변경될때마다 새롭게 인스턴스를 할당하는 것은 매우 비효율적인 행동이다. 반면에 바로 위의 해결한 코드를 살펴보게 되면 useMemo()를 통해서 처음 렌더링 될때 observer에 new IntersectionObserver()으로 인스턴스를 할당하게 되어 프로그램이 처음부터 끝날때까지 하나의 IntersectionObserver인스턴스를 사용하게 된다. 이렇게하면 해당 observer가 다른 변화에 의해 정해둔 옵션이나 트리거가 변경될 위험도 적어지게 되는 것 같다.

 

3. 마침

작성하다보니 문제 해결 보다는 그냥 경험담처럼 작성한것 같다. 많이 부족하고 이제 Hooks에 대해서 배웠기 때문에 해당 문제를 마주쳐서 어려움을 겪고 계신 분들은 아래의 자료들을 참고하면 더 쉽게 해결할 수 있을 것 같습니다.

 

https://velog.io/@shmoon2917/useEffect-%EC%9D%98%EC%A1%B4%EC%84%B1%EC%97%90-ref%EB%A5%BC-%EB%8B%B4%EC%9D%84-%EB%95%8C%EB%A7%88%EB%8B%A4-%EC%B0%9C%EC%B0%9C%ED%95%98%EC%8B%A0-%EB%B6%84%EB%93%A4%EC%9D%84-%EC%9C%84%ED%95%B4

 

useEffect 의존성에 ref를 담을 때마다 찜찜하신 분들을 위해

useEffect에 ref를 의존성으로 추가하는 것이 어떤 문제가 있는지, 해결 방법은 무엇인지 알아봅니다.

velog.io

https://www.youtube.com/watch?v=e-CnI8Q5RY4&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=6

 

 

+ 그래서 해결한 코드는?

이곳에서 확인할 수 있습니다. 

https://github.com/whereisrmsqhs/practice-ReactJS/blob/main/my-movie-app/src/routes/Main.js

 

 

댓글