[React/JS] 캐러셀 구현하기 (Tailwind)

캐러셀!! 계속 구현해보고싶었는데 시간에 밀려 시도를 못해보다가 이번 MBTI 테스트 프로젝트를 만들면서 드디어 적용해봤다

 

기존 코드는 데스크탑 기준으로 너비나 배치 등을 적용해둬서 모바일로 확인했을 때 왼쪽과 같이 조각조각 잘리고 가려지고 ㅋㅋㅋ 이상한 배치가 됐다 반응형 UI도 꼭 적용해보고싶었기 때문에 아예 width 1024 이하인 경우에는 테스트에대한 가이드를 캐러셀로 보여주기로 했다.

 

 

구현 방법

1) 캐러셀로 보여줄 컨텐츠가 담긴 배열에서 인덱스를 증가/감소하며 해당하는 내용의 카드가 가운데에 오도록 한다

2) 이전, 다음 버튼을 눌렀을 때 0번 인덱스에서 이전 버튼을 눌렀거나, 마지막 인덱스에서 다음 버튼을 누른 경우 각각 마지막, 첫 인덱스로 변경한다.

3) 캐러셀이 보여지는 범위를 컨테이너로 감싸고, 컨테이너를 넘어서는 영역은 overflow-hidden으로 보여지지 않게 한다.

4) duration, ease-in-out (tailwind 사용)으로 동작이 지속되는 시간과 속도를 조절하고 translate로 이동할 위치를 지정한다.

 

 

다른 예시들을 참고해서 구현했지만 이해가 잘 되지않아 코드를 그림으로 그려 정리해봤다..

이렇게보니 이해가 된다!!

 

import { useState } from 'react';
import guideText from '../../data/guideText.js';
import GuidePanel from './GuidePanel.jsx';
import Button from '../Input/Button.jsx';

const Carousel = () => {
  const [currentIndex, setCurrentIndex] = useState(0);

  const handlePrev = () => {
    setCurrentIndex((prevIndex) => (prevIndex === 0 ? guideText.length - 1 : prevIndex - 1));
  };

  const handleNext = () => {
    setCurrentIndex((prevIndex) => (prevIndex === guideText.length - 1 ? 0 : prevIndex + 1));
  };

  return (
    <div className='flex flex-col items-center'>
      <div className='hidden lg:flex gap-5 mb-14'>
        {guideText.map((guide, index) => (
          <GuidePanel
            key={index}
            title={guide.title}
            detail={guide.detail}
          />
        ))}
      </div>
      <div className='lg:hidden'>
        <div className='relative w-full max-w-[400px] overflow-hidden'>
          <div
            className='flex transition-transform duration-500 ease-in-out'
            style={{ transform: `translateX(-${currentIndex * 100}%` }}
          >
            {guideText.map((guide, index) => {
              return (
                <div
                  key={index}
                  className='flex-shrink-0 flex justify-center items-center w-full'
                >
                  <GuidePanel
                    title={guide.title}
                    detail={guide.detail}
                  />
                </div>
              );
            })}
          </div>
          <Button
            onClick={handlePrev}
            className='absolute top-1/2 left-0 transform -translate-y-1/2 bg-gray-300 hover:bg-gray-400 text-white p-2 rounded-full'
          >
            ◀
          </Button>
          <Button
            onClick={handleNext}
            className='absolute top-1/2 right-0 transform -translate-y-1/2 bg-gray-300 hover:bg-gray-400 text-white p-2 rounded-full'
          >
            ▶
          </Button>
        </div>
        <div className='flex justify-center mt-4 space-x-2'>
          {guideText.map((_, index) => {
            return (
              <span
                key={index}
                className={`w-3 h-3 rounded-full ${currentIndex === index ? 'bg-blue-500' : 'bg-gray-300'}`}
              ></span>
            );
          })}
        </div>
      </div>
    </div>
  );
};

export default Carousel;

 

- 버튼 클릭 이벤트로 currentIndex를 업데이트

- 캐러셀 영역에서 보이지 않을 부분(현재 보여줄 가이드가 아닌 부분) 부모 요소에 overflow-hidden을 설정해 보이지 않게 함

- 가이드 영역은 부모요소 width의 100%만큼 차지하고 있으므로 인덱스 하나를 넘길 때마다 부모요소 width의 100%씩 이동하게 하면 가이드 영역 한칸의 이동이 됨

- transform: translateX를 이용해 x축 기준 어디에 위치할 것인지 지정하는 것이므로 currentIndex * 100 %만큼 이동하게 함 (0번 인덱스를 보여줄 땐 이동 x, 1번을 보여줄 땐 100% === 한 칸 만큼 왼쪽으로 이동)

- 순식간에 이동되지 않고 이동되는 과정이 보이는 것은 그 이동에 duration을 걸어두었기 때문!