🚨 발생한 문제
- ScrollPicker 컴포넌트의 일관되지 않는 스크롤 동작 확인(데스크탑 마우스 설정에 따라 1칸씩, 3칸씩 스크롤 상이)
- 모바일 퍼스트로 화면 드래그로 스크롤 할때는 문제가 없었으나 웹 뷰 테스트 중 해당 문제가 발생
❓원인
- 사용자의 마우스 감도 설정에 따라 wheel 이벤트의 발생 빈도 차이가 발생해 다르게 동작함
- 높은 감도 설정에서는 작은 휠 움직임으로도 여러 번의 이벤트가 발생
✅ 해결 과정
1. isScrolling ref 사용
const isScrolling = useRef(false);
- 연속 스크롤 입력 시에도 일관된 스크롤을 보장하고 스크롤 중복 실행을 방지하기 위해 사용
2. handleWheelScroll 함수 추가
- 스크롤 속도 최적화 (behavior: 'smooth' 시간 단축, setTimeout 시간 조정)
- 가독성 향상을 위해 유틸리티 함수로 분리
interface ScrollHandlerParams {
containerRef: React.RefObject<HTMLDivElement>;
currentIndex: number;
setCurrentIndex: (index: number) => void;
options: string[];
isScrolling: React.MutableRefObject<boolean>;
handleScroll: (e: React.UIEvent<HTMLDivElement>) => void;
}
export const handleWheelScroll = (
e: WheelEvent,
{
containerRef,
currentIndex,
setCurrentIndex,
options,
isScrolling,
handleScroll,
}: ScrollHandlerParams,
) => {
e.preventDefault(); // 커스텀 스크롤 동작을 구현하기 위해 브라우저의 기본 스크롤 동작 방지
// 이미 스크롤 중이거나 컨테이너 ref가 없으면 함수 실행을 중단 (중복 스크롤 방지)
if (isScrolling.current || !containerRef.current) return;
// 스크롤 상태 설정
isScrolling.current = true;
// 마우스 휠 방향에 따른 새로운 인덱스 계산
const direction = e.deltaY > 0 ? 1 : -1;
const newIndex = Math.max(
0,
Math.min(options.length - 1, currentIndex + direction),
); // Math.max와 Math.min으로 인덱스가 유효범위를 벗어나지 않도록
setCurrentIndex(newIndex); // 새 인덱스로 상태 업데이트
// 부드러운 스크롤 효과
containerRef.current.style.scrollBehavior = "smooth";
containerRef.current.style.transition = "all 100ms";
containerRef.current.scrollTop = newIndex * 40;
handleScroll({
currentTarget: containerRef.current,
} as React.UIEvent<HTMLDivElement>);
// 100ms 후에 스크롤 상태를 false로 설정해서 다음 스크롤 입력을 받을 수 있게 상태 초기화
setTimeout(() => {
isScrolling.current = false;
}, 100);
};
3. wheel 이벤트 리스너를 관리하는 useEffect 추가
- 컴포넌트 마운트 시 + currentIndex, options 변경 시 실행
- wheel 이벤트가 발생할 때마다 wheelHandler 가 handleWheelScroll 함수 호출
useEffect(() => {
const element = containerRef.current;
if (element) {
const wheelHandler = (e: WheelEvent) =>
handleWheelScroll(e, {
containerRef,
currentIndex,
setCurrentIndex,
options,
isScrolling,
handleScroll,
});
element.addEventListener("wheel", wheelHandler, { passive: false }); // 스크롤 이벤트 기본동작 제어
// 컴포넌트 언마운트 or 의존성 값 변경 시 이벤트 리스너 제거
return () => {
element.removeEventListener("wheel", wheelHandler);
};
}
}, [currentIndex, options]);
📌 결과
- 기기 마우스 감도 설정과 관계없이 사용자 스크롤에 정확하게 반응하는 모습을 확인할 수 있다!

'TIL' 카테고리의 다른 글
Next.js 미들웨어(Middleware) (0) | 2025.01.24 |
---|---|
최종 프로젝트 발표 및 부트캠프 수료 후 회고 (1) | 2024.11.26 |
일정등록 모달 구현 방식 개선 (0) | 2024.11.19 |
Tailwind css 로 반응형 구현하기 (1) | 2024.11.18 |
favicon & og 설정하기 (0) | 2024.11.14 |