본문 바로가기
React

[React] 슬라이드 카드 UI 만들기

by teamnova 2025. 5. 15.
728x90

안녕하세요. 오늘은 리액트를 사용해 슬라이드 카드 UI를 만들어보겠습니다.

슬라이드 카드는 이미지, 텍스트 등을 한 화면에서 넘기며 보여줄 수 있는 방식으로,  
포트폴리오, 프로젝트 소개, 리뷰 카드 등 다양한 구성에 활용할 수 있습니다.

 

React에서는 외부 라이브러리 없이도 직접 구현이 가능하지만,  

이번에는 react-slick을 활용해 구현해보았습니다. 

 

 

1. 슬라이드 목록 만들기 

자바 스크립트 배열로 슬라이드 하나하나에 들어갈 제목, 내용, 이미지 등을 담은 "목록"을 생성합니다. 

const cardData = [
  {
    title: "1. Wall Sina",
    description:
      "The innermost wall, where the royal family and nobles reside.",
    image: "https://source.unsplash.com/random/800x400?city",
  },
  {
    title: "2. Wall Rose",
    description: "The middle wall, home to many of the general populace.",
    image: "https://source.unsplash.com/random/800x400?nature",
  },
  {
    title: "3. Wall Maria",
    description:
      "The outermost and largest wall, the first to fall during the Titans' initial attack.",
    image: "https://source.unsplash.com/random/800x400?space",
  },
];

 

 

2. 화살표 버튼 만들기 

JSX 문법을 활용해 html 형식으로 버튼을 만들어줍니다. 

function PrevArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        left: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      ←
    </button>
  );
}

function NextArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        right: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      →
    </button>
  );
}

 

 

3. 전체 UI 조립 

먼저 슬라이더 작동 방식을 setting 해줍니다. 하단 인디케이터 표시 여부, 슬라이드 무한반복 여부, 슬라이드 전환 속도, 한 번에 보여줄 슬라이드 개수 등을 설정합니다. 

 

이후 Slider 컴포넌트 내부에 각 카드 데이터를 html 요소로 랜더링합니

function App() {
  const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1,
    nextArrow: <NextArrow />,
    prevArrow: <PrevArrow />,
  };

  return (
    <div className="slider-wrapper">
      <Slider {...settings}>
        {cardData.map((card, index) => (
          <div key={index} className="slide">
            <div
              className="slide-background"
              style={{ backgroundImage: `url(${card.image})` }}
            >
              <div className="text-box">
                <h2>{card.title}</h2>
                <p>{card.description}</p>
              </div>
            </div>
          </div>
        ))}
      </Slider>
    </div>
  );
}

 

 

4. 자바스크립트 전체 코드 입니다

import "./App.css";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import Slider from "react-slick";

const cardData = [
  {
    title: "1. Wall Sina",
    description:
      "The innermost wall, where the royal family and nobles reside.",
    image: "https://source.unsplash.com/random/800x400?city",
  },
  {
    title: "2. Wall Rose",
    description: "The middle wall, home to many of the general populace.",
    image: "https://source.unsplash.com/random/800x400?nature",
  },
  {
    title: "3. Wall Maria",
    description:
      "The outermost and largest wall, the first to fall during the Titans' initial attack.",
    image: "https://source.unsplash.com/random/800x400?space",
  },
];

function PrevArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        left: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      ←
    </button>
  );
}

function NextArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        right: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      →
    </button>
  );
}

function App() {
  const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1,
    nextArrow: <NextArrow />,
    prevArrow: <PrevArrow />,
  };

  return (
    <div className="slider-wrapper">
      <Slider {...settings}>
        {cardData.map((card, index) => (
          <div key={index} className="slide">
            <div
              className="slide-background"
              style={{ backgroundImage: `url(${card.image})` }}
            >
              <div className="text-box">
                <h2>{card.title}</h2>
                <p>{card.description}</p>
              </div>
            </div>
          </div>
        ))}
      </Slider>
    </div>
  );
}

export default App;

 

 

 

5. CSS 전체 코드 입니다 

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}


.slider-wrapper {
  width: 100%;
  max-width: 900px;
  margin: 50px auto;
  position: relative;
}

.slick-slide > div {
  height: 100%;
}

.slide {
  height: 400px;
}

.slide-background {
  height: 100%;
  width: 100%;
  position: relative;
  background-size: cover;
  background-position: center;
  border-radius: 20px;
  overflow: hidden;
}

.text-box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: rgba(0, 0, 0, 0.6);
  color: white;
  border-radius: 20px;
  padding: 30px;
  width: 80%;
  max-width: 600px;
  text-align: center;
}

 

 

시연 영상입니다