마우스 스크롤 이벤트 처리

스크롤 이벤트 처리를 할 경우, 일반적인 순수 Y축 값을 통해 측정, setTimeout을 사용하여 throttle 처리, requestAnimationFrame

3가지 방법을 각각 비교하여 어떤 차이가 있는지 비교하자.


순수 스크롤 이벤트를 통한 Y축 값 측정

const [scrollFlag, setScrollFlag] = useState(false);

const handleScroll = () => {
    const { scrollY } = window;
    const isScrolled = scrollY !== 0;
    // console.log(scrollY);
    setScrollFlag(isScrolled);
  };

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

ezgif com-gif-maker

스크롤을 내릴때 상당히 많은 값들이 측정된다.

만일 setState이 연쇄적으로 변경이 필요한 함수일 경우 불필요한 재렌더링이 동작되어

브라우저 성능에 악영향을 줄 수도 있다.


setTimeout을 이용한 throttle 처리

  const [scrollFlag, setScrollFlag] = useState(false);

  const updateScroll = () => {
    const { scrollY } = window;
    const isScrolled = scrollY !== 0;
    console.log(scrollY);
    setScrollFlag(isScrolled);
  };
  
    const throttle = (callback, delay) => {
    let timer = null;

    // 클로저 함수 발동
    return () => {
      // 중복 방지 (두번 셋타임 들어가 콜백 부르기에 비효율적 이므로)
      if (timer) return;
      timer = setTimeout(() => {
        callback();
        timer = null;
      }, delay);
      // console.log("###", timer);
    };
  };
  
  const handleScroll = throttle(updateScroll, 100);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

ezgif com-gif-maker

스크롤을 내릴 경우, 100ms 마다 throttle 함수가 동작되어 반환되므로,

빠른 속도로 실행되는 scroll 이벤트를 제어할 수 있다.

그러나 이 방법은 지난 블로그 포스트에서도 언급했듯,

사용자 모니터의 성능에 따라 100ms의 제어를 못할 수도 있게 된다.

물론 단순 Y축 값 0이 아닌 경우에 따라 css 효과가 단순하게 바뀌는 경우, throttle 처리는 큰 문제가 없다.

하지만, 애니메이션 효과가 연쇄적인 css 처리가 진행된다면 큰 문제가 발생한다.


requestAnimationFrame

 const [scrollFlag, setScrollFlag] = useState(false);

  const updateScroll = () => {
    const { scrollY } = window;
    const isScrolled = scrollY !== 0;
    console.log(scrollY);
    setScrollFlag(isScrolled);
  };

  const task = (callback) => {
    let tick = false;

    if (tick) {
      return;
    }

    return () => {
      tick = true;
      return requestAnimationFrame(() => {
        tick = false;
        return callback();
      });
    };
  };

  const handleScroll = task(updateScroll);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

ezgif com-gif-maker (3)

모니터 hz 성능에 알맞도록 더 정교하게 측정되었다.

setTimeout처럼 임의로 시간을 안맞추어도 좀 더 자연스럽게 브라우저가 적절한 타이밍에 조절해주게 된다.

그러나 이 방법은 canvas, svg 등 복잡한 애니메이션 을 다룰 때 쓰이는 방법이지,

단지 스크롤 값을 일시적으로 측정하여 발생하는 트리거라면 오히려 setTimeout이 더 효과적이다.

requestAnimationFrame 사용한 Header 트리거 이벤트

ezgif com-gif-maker (1)

너무 정교하게 잡은 까닭에 state 변경이 처리되기 전, y축 값이 여러번 측정되어 버벅이는 현상을 발견할 수 있다.

setTimeout을 사용한 Header 트리거 이벤트

ezgif com-gif-maker (2)