[리액트] 캐러셀 기능

임승민·2022년 8월 19일
1

배민상회 클론코딩 프로젝트에서 캐러셀 기능 구현을 맡게되었다.
실제 배민상회 처럼 무한 캐러셀을 구현하고 싶었으나 능력안에서 가장 빠르게 만들 수 있는 방법으로 구현하였다.

React

import React, { useState, useEffect } from 'react';
import './Carousel.scss';

const Carousel = () => {
  const [count, setCount] = useState(0); 

  const nextBtn = () => {
    setCount(count => (count < 9 ? count + 1 : (count = 0)));
  };
  const prevBtn = () => {
    setCount(count => (count > 0 ? count - 1 : null));
  };

  useEffect(() => {
    const interval = setTimeout(() => {
      nextBtn();
    }, 3000);
    return () => clearTimeout(interval);
  });

  return (
    <div className="carousel">
      <div className="container">
        <div
          className="images"
          style={{ transform: `translateX(${count * -1080}px)` }}
        >
          <img src="/images/carousel/1.png" />
          <img src="/images/carousel/2.png" />
          <img src="/images/carousel/3.png" />
          <img src="/images/carousel/4.png" />
          <img src="/images/carousel/5.png" />
          <img src="/images/carousel/6.png" />
          <img src="/images/carousel/7.png" />
          <img src="/images/carousel/8.png" />
          <img src="/images/carousel/9.png" />
          <img src="/images/carousel/10.png" />
        </div>
      </div>
      <button className="prev" onClick={prevBtn}></button>
      <button className="next" onClick={nextBtn}></button>
      <div className="chapter">
        <p>{count + 1} / 10 </p>
      </div>
    </div>
  );
};
export default Carousel;
  1. 우선 이미지 10장을 가로로 나열해줄 images div를 만든다.
  2. 하나의 이미지만 보이고 나머지는 가리기 위해 이미지 크기만큼의 width를 가진 container div를 만든다.
  3. 캐러셀과 버튼, 챕터를 감싸줄 carousel div를 만든다.
  4. count useState를 만들어 prev버튼은 -1 next버튼은 +1을 해준다. 계산을 위해 초기값을 0으로 준다.
  5. imagestransform:translateX(${count * -1080}px) 을 줘서 count가 올라가면 x축으로 -1080px(이미지크기) 만큼 이동하게 한다.
  6. next 함수로 count 9에서 next함수를 실행하면 count를 0으로 만들어서 처음 이미지로 넘어간다.(첫 이미지 count는 0이다.)
  7. chapter는 count+1 / 10 으로 최대 이미지는 임으로 정의한다.

useEffect

setTimeout을 설정하고 next 버튼을 누르면 setTimeout이 중첩되서 연속적으로 실행되는 문제가 있었다.
count state를 공유하고 있어서 문제가 발생한다 판다해서 useEffect로 옮기고 clearTimeout으로 cleanUp을 설정해서 setCount 실행으로 리렌더링 시 인자속 setTimeout을 종료 시키고 새로 실행하여 setTimeout 중첩 문제를 해결하였다.

Scss

@import '../../styles/variables.scss';
.carousel {
  width: 1080px;
  position: relative;
  .container {
    width: 1080px;
    height: 303px;
    overflow: hidden;
    .images {
      display: flex;
      height: 303px;
      transition: transform 0.5s;
      img {
        width: 1080px;
        height: 303px;
        &:hover {
          transition: 0.3s;
          transform: scale(1.05);
        }
        &:not(hover) {
          transition: 0.3s;
        }
      }
    }
  }
  .prev,
  .next {
    position: absolute;
    top: 140px;
    width: 28px;
    height: 28px;
    border: none;
    background-color: transparent;
    font-size: 30px;
    color: white;
  }
  .prev {
    left: 10px;
  }
  .next {
    right: 10px;
  }

  .chapter {
    @include flex(center, center);
    position: absolute;
    bottom: 10px;
    right: 10px;
    width: 61px;
    height: 31px;
    border-radius: 30px;
    background-color: rgba(0, 0, 0, 0.456);
    color: white;
    font-weight: bold;
  }
}

레이아웃 적인 속성이 대부분이여서 이대로 하면 완성 캐러셀 처럼 구현이 가능하다.

Images에 transition: transform 0.5s 로 넘어가는 속도를 조절하였다.

img에 transition: 0.3s; transform: scale(1.05); 로 Img에 마우스를 올리면 img가 일정속도로 커지는 속성을 줬다. 또 &:not(hover) { transition: 0.3s; } 로 Img에 커서를 때면 일정속도로 다시 돌아온다.

마무리

2달전 쯤 바닐라JS로 캐러셀을 만든 적이 있는데 리액트로 다시 만들어 보려 하니 많이 막막했다.
하지만 오히려 JS보다 더 간결하게 코드를 작성할 수 있고 더 직관적이다고 느꼈다.
조금 성장한 기분도 들었지만 무한 캐러셀을 구현할 시간이 부족해 많이 아쉽다.

0개의 댓글