Next.js에서 Carousel 컴포넌트를 구현해보자 (Web)

Molly·2023년 6월 10일
12

React, Next.js

목록 보기
5/7

구현해보고자 하는 Carousel 컴포넌트의 특징은 다음과 같다.

1. 사용자의 화면 width에 따라, 3개 혹은 4개의 카드가 보여진다.
2. 3초 마다 카드가 오른쪽으로 이동하고 마지막에 다시 원 상태로 돌아온다. 
3. 사용자는 화살표 버튼을 클릭해서 카드를 한 개씩 넘길 수 있다.  
4. 사용자의 화면 width를 원활하게 추적하기 위해, custom hook을 활용할 것이다. 


Custom Hook


사용자의 화면을 원활하게 추적하는 custom hook인 useWindowWidth를 활용하고자 한다.

/utils/useWindowWidth.ts

import { useState, useEffect } from 'react';

function useWindowWidth() {
    const isClient = typeof window === 'object';
    const [windowWidth, setWindowWidth] = useState(isClient ? window.innerWidth : 0);
  
    useEffect(() => {
      if (!isClient) return; // SSR일 경우 리턴합니다.
  
      function handleResize() {
        setWindowWidth(window.innerWidth);
      }
  
      window.addEventListener('resize', handleResize);
      handleResize();
  
      return () => window.removeEventListener('resize', handleResize);
    }, [isClient]); // isClient의 변경에 따라 훅을 실행합니다.
  
    return windowWidth;
  }

export default useWindowWidth;
  1. Next.js 프로젝트이므로, SSR 상황에서 오류가 발생하지 않도록 조건문을 작성했다.

  2. 의존성 관리를 위해, handleResize 함수를 useEffect 내부에 작성했다.

  3. 메모리 누수를 방지하기 위해, 컴포넌트가 unmount 될 때, 이벤트 핸들러를 제거했다.




구현한 Carousel 컴포넌트는 다음과 같다.


import Link from 'next/link';
import Image from 'next/image';
import { useState, useEffect } from 'react';
import left from 'assets/icons/main/left.svg';
import right from 'assets/icons/main/right.svg';
import AdCard from './AdCard';
import WeatherCards from './WeatherCards';
import { CarouselData } from 'interfaces/main';
import useWindowWidth from 'utils/useWindowWidth';

interface Props {
  carouselData: CarouselData[];
}

const Carousels = ({ carouselData }: Props) => {
  const width = useWindowWidth();
  const [index, setIndex] = useState(0);

  useEffect(() => {
    let timeout: NodeJS.Timeout;
    timeout = setTimeout(
      () =>
        index * 4 > carouselData?.length
          ? setIndex(0)
          : setIndex((prev) => prev + 1),
      3000
    );
    return () => clearTimeout(timeout);
  }, [index, carouselData.length]);
  
  return (
    <div className="flex items-center  gap-5 justify-center min-w-[1024px] max-w-1320 mx-auto">
      <Image
        src={left}
        alt=""
        className={`w-10 cursor-pointer 
        ${index === 0 && 'opacity-0'} `}
        onClick={() => index > 0 && setIndex((prev) => prev - 1)}
      />
      <div className="overflow-hidden">
        <div
          className={`flex gap-6 ease-in-out duration-1000 `}
          style={{ transform: `translateX(calc(${index}*-612px))` }}
        >
          {carouselData?.map((asset: CarouselData, i: number) => (
            <div key={i}>
              {(width > 1024 ? (i + 1) % 4 === 0 : (i + 1) % 3 === 0) ? (
                <AdCard />
              ) : (
                <Link href={`/detail/${asset['ITEM_CD_DL']}`}>
                  <WeatherCards
                    Exp_CVaR={asset['EXP_CVaRNTS']}
                    exchg={asset['HR_ITEM_NM']}
                    loc={asset['LOC']}
                    krName={asset['ITEM_KR_NM']}
					.... 
                  />
                </Link>
              )}
            </div>
          ))}
        </div>
      </div>
      <Image
        src={right}
        alt=""
        className={`w-10 cursor-pointer
        ${index * 4 > carouselData?.length && 'opacity-0'} `}
        onClick={() =>
          index * 4 < carouselData?.length && setIndex((prev) => prev + 1)
        }
      />
    </div>
  );
};

export default Carousels;
  1. 사용자 화면의 width를 추적하기 위해 custom hook을 불러왔다.

  2. transform 속성을 활용하기 위해, inline css스타일인 style을 활용했다.
    (tailwind css를 활용하므로, 고 수준의 css속성을 다루는데 한계가 있기 때문)

  3. translateX로 인해, index의 변화에 따라 612px만큼 X축 방향으로 이동하게 된다.

  4. index * 4 < carouselData?.length를 통해, 끝에 다달았을 때, 무엇을 할 지 결정한다.
    (ex. index * 4 > carouselData?.length ? setIndex(0) : setIndex((prev) => prev + 1) 이 경우 카드가 끝에 다다랐을 때, index가 다시 0이 되어 처음으로 돌아오고 이외에는 index값을 더하며 카드를 오른쪽으로 이동시킨다.

  5. setTimeout을 활용하여 3초마다 카드가 오른쪽으로 이동하도록 설정했다.



마무리


이번 포스팅에서 Web View에서의 Carousel 컴포넌트를 구현해보았다. Carousel 컴포넌트는 사용자에게 보여주고 싶은 정보들을 보기 편한 UI로 가독성있게 전달하고자 할 때 자주 활용되는 컴포넌트이다.

다음 포스팅에는 Mobile View에서의 Carousel 컴포넌트를 구현해보고자 한다.

profile
Next.js, Typescript, Rust

2개의 댓글

comment-user-thumbnail
2023년 6월 17일

좋은글감사합니다!!!!

답글 달기
comment-user-thumbnail
2023년 6월 19일

잘봤습니당~

답글 달기