어제 가로스크롤을 구현했다. 구현하면서 스크롤 이벤트에 throttling이나 debounce같은 기능을 추가해 마우스가 움직일때마다 이벤트 방지를 하는 것을 방지하려고 했다. 그래서 스크롤이벤트에 가장 많이 사용하는 스로틀링을 알아보고 적용해보기로 했다.
디바운스와 같이 연속적인 동작을 방지하기 위한 기능이다. 우선 디바운스는 지정된 시간안에 동작하는 이벤트에 대해서 동작하지 않고 마지막에 동작하는 이벤트를 적용하는 기능이였다.
const useDebounce = (value: string, time: number) => {
const [deBounceValue, setDeBounceValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => {
setDeBounceValue(value)
}, time)
return () => {
clearTimeout(timer)
}
}, [time, value])
return { deBounceValue }
}
그래서 내가 만든 커스텀 훅을 보면 setTimeout을 이용해 지정한 시간이 지난 이후에 value를 세팅하고 리턴하는 것을 볼수가 있다.
스로틀링은 조금 다르다. 연속적인 이벤트가 이뤄질때 지정한 시간 간격으로 동작하는 것이다. 만약 10초에 100번 연속으로 동작하는 이벤트가 있다고 가정했을때 스로틀링을 사용해 1초라는 시간을 지정하면 지정한 시간 간격으로 동작하기 때문에 불필요한 요청을 생략해 10번만 동작하는 이벤트가 된다.
이렇게 연속적인 이벤트는 마우스 이동이나 스크롤과 같은 기능에 사용한다.
const onDragMove = (e: MouseEvent) => {
setValue(...)
}
...
<div onMouseMove={onDragMove}>
만약 이런 코드가 있다고 가정했을대 마우스가 움직일때마다 해당 함수를 호출해서 state를 변경할 것이다.
const mock = [1,2,3,4,5] //offset:5의 데이터
...
const onScroll = (e: MouseEvent) => {
setOffset((prev)=>prev+5)
}
또한 스크롤이벤트로 스크롤시에 데이터가 5개씩 더 불러오는 코드가 있다고 했을때 스크롤 한칸마다 데이터가 계속 불러와 질것이다. 이럴때 사용하는게 throttling이다. 코드는 간단하다.
const throttle = (func: () => void) => {
let timer
if (!timer) {
timer = setTimeout(() => {
timer = null
func()
}, 1000)
}
}
여기에서도 setTimeout을 사용해서 일정 시간 이후에 파라미터로 전달한 함수를 실행시켜주는 것이다. 위의 함수는 1초에 한번씩 이벤트를 동작하는 것이다. 실제로 적용하는 방법도 간단하다.
const onAxisDragMove = (e: MouseEvent) => {
if (!isDrag) {
return
}
throttle(() => {
preventUnexpectedEffects(e)
const scrollLeft = totalX - e.clientX
if (scrollRef.current && 'scrollLeft' in scrollRef.current) {
scrollRef.current.scrollLeft = scrollLeft
}
})
}
전에 만들어둔 드래그이벤트의 동작 함수를 throttling으로 처리하는 것이다.
아쉽게도 내가 사용하는 스크롤 이벤트에서는 유의미한 차이가 없었다. 왜냐하면 해당 시간에 한번씩 동작하는 기능인데 계속해서 상태를 바꾸는 데이터자체가 없다. 위의 함수에서 실시간으로 변화하는 값은 div태그의 scrollLeft값인데 해당 값이 스로틀링에 의해 지연되서 동작한다면 사용자가 동작하는 시간보다 훨신 늦게 동작할 것이다.
그래서 우선은 100mms로 거의 차이가 없도록 만들어뒀다. 실제로 적용하면서 느낀점은 스크롤 이벤트 자체에 스로틀링을 걸기보단 스크롤에 의해 데이터를 불러오거나 api를 요청하는 경우에 사용하면 효과적일것 같다.
또한 무한 스크롤 구현에서 옵저버가 아니라 스로틀링을 사용하면 더 깔끔하게 구현되지 않을까하는 생각이 든다. 하지만 아직까진 옵저버로 구현하는게 불필요한 요청을 하지 않는다는 것은 확실하다.
이번 기능은 큰 변화를 이끌어 내지 못해서 조금 아쉽다. 하지만 함수와 값을 딜레이시켜서 동작하고 바꾸는 방법을 익힌 점에서 성과를 얻었다. 저번에 디바운스 이후에 스로틀링을 못써볼줄 알았는데 어찌저찌 체험은 했다!
나중에 필요한 부분에서 사용하면 매우 유용하지 않을까 싶다.
이제 더 자잘한 부분을 수정중이다. 대부분 스타일과 관련된 코드이고 가끔씩 페이지에 잘못된 접근에 의한 처리가 안되있어서 그런 부분을 열심히 찾고 있다. 물론 너무 꼼꼼히 할 필요가 있나 싶지만 결국에는 해야하는 일이니까 최선을 다하는 중이다.
이후에 수정하는 내용도 정리해보겠다.