[React] UseState로 슬라이더 기능 만들기

8955 단어 ReactReact


위솝 프로젝트를 하는 동안 외부 라이브러리를 사용할 수 없기에, 배웠던 걸 최대한 활용하여 슬라이드 기능을 구현해보았다. 일단 바로 생각났던 것은 이전에 useState를 처음 배웠을 때 만들어 봤던 카운터! 기본 로직은 아래와 같이 생각했다.

차근차근 생각해본 기본 로직

  1. CSS속성 중 translateX를 활용하는데, 사용자가 버튼을 누를때마다 한 상품의 넓이만큼 이동해야 한다.
  2. 버튼을 누를때마다 translateX(-(상품의 넓이 * 버튼 클릭 수)) 에서 버튼 클릭 수를 변경해주면 원하는 픽셀 만큼 움직일 것이다!
  3. 따라서 count라는 state를 하나 선언해주고, 오른쪽 버튼을 누를 때에는 +1씩 증가, 왼쪽 버튼을 누를 대에는 -1씩 감소하도록 만들어준다.
  4. 상품의 맨 처음, 혹은 맨 끝으로 가면 사용자가 더 이상 버튼을 볼 수 없도록 버튼을 조건부렌더링을 해준다.

구현한 코드

1. state 선언 및 증가, 감소로 state 제어하기

 const [counter, setCounter] = useState(0);

  const leftBtnClickHandler = () => {
    setCounter(counter - 1);
  };

  const rightBtnClickHandler = () => {
    setCounter(counter + 1);
  };

2. 조건부 렌더링을 위한 로직 작성하기

1) 왼쪽 버튼은 카운터가 0보다 클 때 true가 되도록 만든다.
3) 오른쪽 버튼은 상품의 수가 3보다 크면서, 상품목록 -3이 될 때까지만 true가 되도록 만든다.
(상품의 수가 3개일 때까지는 화면에 보이기 때문에 별도로 버튼이 필요없고, 상품목록 -3 이상으로 버튼을 누를 수 있도록 하면 상품 목록의 끝까지 버튼을 누른 후에도 공백이 더 나오기 때문에 제어해 준다.)

  let showLeftBtn = counter > 0;
  let showRightBtn = counter !== products.length - 3 && products.length > 3;

3. 위에서 만든 조건에 따라 style속성을 이용하여 translateX를 만들어주고, 버튼을 조건부렌더링을 해준다.

  <div className="productBodyScrollable">
       <div
         className="products"
         style={{ transform: `translateX(-${27 * counter}%)` }}
       >
         {products.map(product => {
           return <Product key={product.productId} {...product} />;
         })}
       </div>
       {showLeftBtn && (
         <div className="carouselLeft">
           <button
             name="left"
             className="carouselLeftBtn"
             onClick={leftBtnClickHandler}
           >
             <i className="fa-solid fa-chevron-left" />
           </button>
         </div>
       )}
       {showRightBtn && (
         <div className="carouselRight">
           <button
             name="right"
             className="carouselRightBtn"
             onClick={rightBtnClickHandler}
           >
             <i className="fa-solid fa-chevron-right" />
           </button>
         </div>
       )}
     </div>

마무리하며

구글링을 해 보니 useRef를 사용한 코드들이 많이 있었다. 그러나 공식문서에 따르면 DOM.focus()등의 접근이 필요한 어쩔 수 없는 경우를 제외하고는 DOM에 직접 접근하는 방식이 좋지 않다고 하여 기존에 사용했던 것들만으로 만들어보긴 했는데 더 좋은 접근 방식이 있는지 동기들의 코드를 살펴보며 참고해봐야 겠다.
추가로, 위 기능을 만들며 가장 시간을 많이 쓴 것은 의외로 CSS였는데, 두 가지 문제가 있었다.

  1. 상품이 화면 뒤로 길게 빠지지 않고 화면에 맞게 줄여지는 문제 - display flex대신 white-space :nowrap을 통해 상품이 화면 크기에 맞게 줄어들지 않고 나열되도록 만들어주었다. 이 속성을 몰랐다면 한참 더 고생할 뻔한 걸 두두님께서 친히 친절하게 알려주셨다.

  .productBodyScrollable {
    position: relative;
    width: 78.8%;
    height: 100vh;
    white-space: nowrap;
    overflow: hidden;
  1. 슬라이더 끝 부분이 작은 화면에서는 정상적으로 끝나는데 큰 화면에서는 원하는 곳에서 끝나지 않고 버튼을 더 누를 수 있었던 문제
    CSS를 작성할 때 px 단위로 작성했던게 문제가 되었다. px대신 모든 width를 vw속성을 이용하니 해결 되었다!

좋은 웹페이지 즐겨찾기