트러블슈팅

[React] 가로 스크롤 구현하기(feat. Typescript, Tailwind)

emmaOH! 2024. 11. 10. 23:08

개발을 하다 보면 일반적인 세로 방향 스크롤이 아닌 아래의 그림처럼 가로 방향으로 넘어가는 스크롤이 필요한 경우가 있습니다.

가로 방향으로 넘어가는 스크롤

따라서 이번 글에서 가로 스크롤에 필요한 코드를 정리해보겠습니다.

 


🐝 전체 코드

아래의 코드는 'useHorizontalScroll.ts'라는 이름의 커스텀 훅으로, 원하는 요소에 가로 스크롤을 적용합니다.

import { useRef, useEffect, useCallback } from 'react';

export const useHorizontalScroll = () => {
  const listWrapperRef = useRef<HTMLUListElement>(null);

  const handleWheel = useCallback((e: WheelEvent) => {
    const container = listWrapperRef.current;
    
    if (container) {
      const delta = e.deltaY;
      container.scrollLeft += delta;
      e.preventDefault();
    }
  }, []);

  useEffect(() => {
    const container = listWrapperRef.current;
    
    if (container) {
      container.addEventListener('wheel', handleWheel);
      
      return () => {
        container.removeEventListener('wheel', handleWheel);
      };
    }
    
    return () => {};
  }, [handleWheel]);

  return listWrapperRef;
};

코드는 크게 네 부분으로 나눌 수 있으며 아래에서 자세히 살펴보겠습니다.

 


🐝 코드 설명

1) 'listWrapperRef' 생성

const listWrapperRef = useRef<HTMLUListElement>(null);
  • 스크롤이 적용될 요소는 보통 리스트 요소(<ul>)이므로 useRef 훅을 사용하여 'listWrapperRef' 변수를 생성합니다.
  • 해당 ref는 스크롤이 적용될 요소와 연결됩니다.

 

2) 'handleWheel' 함수 정의

const handleWheel = useCallback((e: WheelEvent) => {
  const container = listWrapperRef.current;
    
  if (container) {
    const delta = e.deltaY;
    container.scrollLeft += delta;
    e.preventDefault();
  }
}, []);
  • 'handleWheel' 함수가 받는 event(e)인 WheelEvent는 사용자의 마우스 휠 이벤트 객체입니다.
  • 마우스 휠이 위아래(세로 방향)로 움직인 정도인 deltaY 값을 연결된 요소의 scrollLeft 속성에 더해, 해당 요소가 가로로 스크롤되도록 합니다.
  • e.preventDefault()를 호출하여 기본 세로 스크롤 이벤트를 막습니다.
참고로 'const delta'를 정의할 때 'e.detail' 또는 'e.wheelDelta' 속성이 사용되는 경우가 있습니다.
이는 과거에 사용되던 속성으로 최신 브라우저 표준에서는 지원하지 않습니다. 따라서 'deltaX', 'deltaY', 'deltaZ' 속성을 사용하면 됩니다.

 

3) useEffect로 이벤트 리스너 등록

useEffect(() => {
  const container = listWrapperRef.current;
    
  if (container) {
    container.addEventListener('wheel', handleWheel);
      
    return () => {
      container.removeEventListener('wheel', handleWheel);
    };
  }
    
  return () => {};
}, [handleWheel]);
  • useEffect 훅으로 컴포넌트가 마운트 될 때 'wheel' 이벤트 리스너를 등록하고,
  • 원마운트될 때 해당 이벤트 리스너를 제거하여 메모리 누수를 방지합니다.

 

4) 'listWrapperRef' 반환

return listWrapperRef;
  • 마지막으로 앞서 생성한 'listWrapperRef'를 반환하여 가로 스크롤이 필요한 요소에 ref를 연결할 수 있도록 합니다.

 


🐝 적용하기

원하는 요소에 가로 스크롤을 적용하기 위해 제가 구현한 코드의 일부를 예시로 보겠습니다.

 

폴더 목록이 <ul> 태그로 감싸져 있고, <li> 태그로 감싸진 폴더 항목들이 map()을 통해 화면에 뿌려집니다.

가로 스크롤과 관련되지 않은 코드는 생략하였습니다.

import { useHorizontalScroll } from '@/lib/hooks';

export const FolderList = () => {
  const listWrapperRef = useHorizontalScroll();

  //... 생략
  
  return (
    <div>
      <ul
        className="overflow-x-auto scrollbar-hide"
        ref={listWrapperRef}		// 여기에 연결
      >
        {folderList.map((folder) => (
          <li key={folder.id}>
            <Folder folder={folder} />
          </li>
        ))}
      </ul>
    </div>
  );
};
  • 위에서 만들어준 'useHorizontalScroll.ts' 파일을 임포트해옵니다.
  • useHorizontalScroll() 커스텀 훅에서 반환하는 'listWrapperRef'를 받아옵니다.
  • 가로 스크롤을 적용하고 싶은 요소에 ref 속성을 활용하여 'ref={listWrapperRef}'를 등록해줍니다.

 


읽어주셔서 감사합니다:)