마우스 오버시 커서를 따라다니는 툴팁을 구현하며
리팩토링하는 과정을 간결하게 기록해보았다
이제는 어떻게 라는 질문이 머리속에 제일 먼저 맴돌기 시작한다
오늘 아침에 출근하면서 본 토스 컨퍼런스의 인트로 영상 문구가 생각났다
내가 좀 더 고생해야 사용하는 유저의 시간을 아낄 수 있다
최적화라는 단어에 대해 고심하게 되는 시기가 온 것이다
내가 좀 더 고생해야 사용하는 유저의 시간을 아낄 수 있다
JavaScript - 마우스 커서에 달린 툴팁 최적화하기 (mousemove)
첫번째 이슈 - useState
mousemove 이벤트를 통해 실시간으로 좌표값을 받아와 useState 에 저장하여 → 툴팁에 style 값으로 넣어주는 방식을 사용하였다. 그렇게 되니 이슈가 생겼다. mousemove 시에 계속 함수를 호출되어 그안의 setState 가 호출되어 렌더링을 일으킨다.
state 가 바뀌면 화면 렌더링을 일으키는데 좌표같은 세세한 값이 매순간마다 저장되니 성능에 좋지 않았다
// 커서 좌표 state
const [cursorPosition, setCursorPosition] = useState<CursorPosition>({
x: 0,
y: 0,
});
// 실시간 좌표 값을 엘레멘트에 반영
const mouseCursor = useMemo(() => {
return (
<div
className="tooltip"
style={{
left: `${cursorPosition.x}px`,
top: `${cursorPosition.y - 30}px`,
}}
>
{`${now.hours} : ${now.minutes} : ${now.seconds}`}
</div>
);
}, [cursorPosition, now.hours, now.minutes, now.seconds]);
// 마우스 움직일 때 실시간 좌표값을 저장해주는 함수
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
const scrollTop = clockRef.current?.scrollTop || 0;
setCursorPosition({
x: e.clientX,
y: e.clientY + scrollTop,
});
};
그래서 state 를 사용하지 않고 직접 돔을 변경하였다.
직접 돔을 변경하게되면 프로젝트가 커지게 되면 나중에 돔컨트롤이 어려울 수 있다는 단점이 있지만,
마우스 무브시 매순간 state 가 렌더링이 되는 것보다는 비용이 적다고 생각하였기 때문이다.
그렇게 되니 코드가 한결 간결해졌다. useState 와 useMemo 를 사용하지 않게 되었다.
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
const scrollTop = clockRef.current?.scrollTop || 0;
const tooltip = document.querySelector(".tooltip") as HTMLElement;
// 직접 style 에 좌표값을 추가
tooltip.style.left = `${e.clientX}px`;
tooltip.style.top = `${e.clientY + scrollTop - 30}px`;
};
두번째 이슈 - Throttling
마우스 무브 이벤트가 일어날 때마다 state를 제거하여 불필요한 렌더링은 사라졌지만 매순간 함수는 호출된다.
역시 불필요한 호출이다. 이 툴팁의 움직임도 애니메이션이기 때문에 1초당 60fps 가 보여지는 것이 적당하다고 생각했다.
그래서 throttling 을 사용해보기로 했다. throttling 은 연속으로 호출되는 이벤트 함수의 경우 매우 유용하게 사용될 수 있다.
그래서 현재의 시간과 지정 타이머의 시간을 비교하여 연속으로 실행하는 useThrottle 훅을 통해
마우스무브함수가 16.6밀리초마다 실행되도록 하여 60fps 에 맞추어 필요한 만큼만 호출해주었다
BUT
useThrottle 도 함수이기 때문에 mouseMove 이벤트가 호출될 때 이 함수 또한 계속 호출된다. 결국 return 값으로 타이머에 따라 실행되도록 했지만 호출은 똑같이 된다. 또한 내부에서 setState 를 하기때문에 state 또한 바뀌어 렌더링이 된다.
throttling 기법을 사용할 떄는 비용을 생각해야 한다. (무거운 쿼리를 사용하는 리스트 api 호출시 사용하는 편이 더 나을 것 같다)
1. 마우스 이벤트 핸들러에 의해 usethrottle 훅이 매번 호출
2. 매번 호출되면서 setState 를 하게 됨 - 일정 시간 이후마다 setState 를 함
3. 즉 첫번째 useMemo 를 쓰는 방법보다는 낫지만 그냥 마우스함수 내에서 직접 접근을 통해 돔 style 핸들링 하는 편이 더 나을 수 있다
useThrottle 훅 >> setState >> mouseMove함수 >> DOM 스타일링
mouseMove함수 >> DOM 스타일링
'JavaScript' 카테고리의 다른 글
알고리즘 - 알고리즘이란 (0) | 2024.05.11 |
---|---|
JavaScript - Debugger 사용기 (Feat. 크롬 개발자 도구) (1) | 2024.03.27 |
JavaScript - 애니메이션 최적화하기 (requestAnimationFrame) (0) | 2024.02.01 |
JavaScript - Argument 란 (Feat. caller, callee) (2) | 2023.11.19 |
JavaScript - 표현식 (2) | 2023.11.17 |
댓글