본문 바로가기
dev/code review

[React] Carousel 기능 구현

by masankong 2023. 5. 31.

style /  api연동 까지 완료한 가상의 WVM 쇼핑몰 main page

 

위 쇼핑몰 메인페이지 기능구현 중 상단 이벤트 배너를 Carousel 기능으로 구현하려고 한다!🧐

📍Carousel  의 사전적인 의미는 회전목마로, 회전목마가 빙빙 도는 것처럼 이미지나 텍스트의 슬라이드를 가로로 슬라이드시켜 여러 개를 표시하는 컴포넌트이다.

 

실은 Carousel 을 편하게 만들 수 있는 라이브러리는 많지만 여러가지 기능이 들어가 있어 React공부에 많이 도움이 될거 같아 라이브러리를 쓰지 않고 직접 하나하나 코드를 짜보려고 한다 (그래서 탈도 많고 에러도 많고 나도 울 예정)

 

나중에 볼지도 모르니 정리해 놓는 Carousel 라이브러리

https://swiperjs.com/demos

 

Swiper Demos

Swiper is the most modern free mobile touch slider with hardware accelerated transitions and amazing native behavior.

swiperjs.com

https://www.embla-carousel.com/

 

A lightweight carousel library with fluid motion and great swipe precision | Embla Carousel

 

www.embla-carousel.com

 

본격적으로 들어가기전 어떤 기능들을 구현해야할지 기능 명세를 짜봤다 Carousel 하나를 구현하는데 꽤 긴 싸움을 해야할것 같다는 느낌이 왔다.. 하지만 그만큼 코드를 만질수 있으니 오히려 좋다라고 생각하고 임해보자..!🥲

Carousel 기능 명세

1. event banner 더미 데이터 생성
  1 - 1 :  데이터 안에 들어가야할것들 :   이미지 URL, src, 대체 텍스트, id

2. 이미지가 event banner 에 노출되고 각이미지 마다 클릭 이벤트시 해당 page 로 이동
  2 - 1 : 이미지가 가로로 길게 정렬되어 view port 영역에서만 노출
  2 - 2: 해당 이미지를 눌렀을때 타겟 이미지의 경로로 이동
  2 - 3: 이미지 밑 '자세히 보기' 버튼을 눌러도 해당 page로 이동

3. next, prev btn 클릭시 이미지가 사용자가 원하는 이미지로 넘어감
  3 - 2 : 마지막 장일때 next 버튼 누르면 첫 이미지로 넘어감
  3 - 3 : 첫번째 장일때 prev 버튼 누르면 마지막 이미지로 넘어감

4. 인디케이터로 생성
  4 - 1 : 인디케이터 클릭시 이미지가 사용자가 원하는 이미지로 넘어감
  4 - 2 : 이미지 넘어갈때 인디케이터도 같이 변경

5. 일정 시간이 지나면 자동으로 다음 이미지로 넘어감
  5 - 1 : 이미지 넘어갈때 인디케이터도 같이 변경

6. 인터렉션 구현 //* 추후 추가 예정
  6 - 1 : 이미지가 하나로 붙어있는 상태에서 옆으로 슬라이드 애니메이숀
  6 - 2 : next, prev 버튼 호버시 진해지거나 나타는 인터렉션 

 

 

1. event banner 더미 데이터 생성

추후에 가지고 있는 데이터 개수 만큼 캐러셀이 작동해야하므로 객체형식으로 이미지 데이터를 생성했다. 그 안에는 '마켓컬리' 에서 가져온 배너 img-url와 이미지가 불러와지지 않았을 경우 불러옴 대체 텍스트, 이미지를 눌렀을 경우 넘어갈 src, key 값으로 전달할 id 가 들어있다. 

const carouselImages = [
    {
        id: 0,
        img_url:
        "https://product-image.kurly.com/cdn-cgi/image/quality=85/banner/main/pc/img/6c201c75-148c-4683-881d-47f56749a9d2.png",
        img_name: "이미지1",
        src: "/",
    },
    {
        id: 1,
        img_url:
        "https://product-image.kurly.com/cdn-cgi/image/quality=85/banner/main/pc/img/5190f93d-5ccb-42be-871a-49307d7cda25.png0",
        img_name: "이미지2",
        src: "/",
    },
    {
        id: 2,
        img_url:
        "https://product-image.kurly.com/cdn-cgi/image/quality=85/banner/main/pc/img/1160c655-83e8-4a4d-841e-183ef8c64c6e.jpg",
        img_name: "이미지3",
        src: "/",
    },
];

export default carouselImages;

이후에 만들어져 있는 Eventbanner 컴포넌트 안에 import 한 뒤 잘 불러와는지 확인을 해봤다!

 

배열로 잘 넘어온 carousel mock 데이터

 

2. 이미지 event banner 에 노출 및 클릭시 해당 page 로 이동

이제는 불러온 데이터의 개수만큼 화면에 노출시키는 작업을 해봐야한다!

일단 styled 컴포넌트로 이미지들을 묶어주는 article 을 생성하고 그 안에 img 태그를 담은 a 태그를 데이터의 갯수만큼 생성했다.

📍a 태그 대신 Link를 쓴 이유는 React 에서는 일반적으로 react-router-dom 패키지를 이용하여 페이지 전환을 하지만 일반 html에서는 <a>태그를 이용해서 페이지 전환을 하면서 애플리케이션이 들고 있던 상태들을 모두 날려버리기 때문에 Link 태그를 사용했다.

// style

const StyledLink = styled(Link)``;

const Banner = styled.article`
    width: 100vw;
    height: 500px;
    background-color: red;
    display: flex;
    overflow: hidden;
`
const BannerImg = styled(StyledLink)`
    background-color: blue;
    
    img {
        width: 100vw;
        height: 100%;
        object-fit: cover;
    }
`

// 마크업

export default function EventBanner() {
    return (
        <Banner>
            {carouselDate.map(img => {
                return <BannerImg to={img.src} key={img.id}>
                    <img src={img.img_url} alt={img.img_name} />
                </BannerImg>
            })}
        </Banner>
        )

이미지를 불러 온 상태

 

현재 이미지를 클릭시 이동하는 경로를 "to={img.src} 로 불러왔기 때문에 각 이미지를 누르면 해당 페이지로 이동을 하게된다.

3. next, prev btn / 인디케이터 클릭시 이미지가 사용자가 원하는 이미지로 넘어감

 ❗️ 문제는 내가 map으로 데이터를 불러오게 되면 한번에 많은 양의 데이터를 불러오기도 하고 js로 문제를 해결하는것이 아닌 css overflow로 해결하는 것이기 때문에 좋은 방법이 아니라고 해서 'useState'로 인덱스 값을 저장하는 방법을 사용해보려고 한다.

구상한 방법은 next버튼을 클릭했을 경우 이미지 데이터의 length 값을 + 1 해서 불러오고 prev 버튼을 눌렀을 경우 이미지 데이터 값을 -1 해주는 식으로 진행하기로 결정했다.

(버튼이 있어야 작동을 할 수 있기 때문에 next, prev 버튼을 추가한뒤 거기에 onClick 을 주었다) 

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

    const handlerNext = () => {
        setCurrentIndex((nextIndex) => nextIndex === carouselDate.length - 1 ? 0 : nextIndex + 1
        );
    }

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

+ 추가로 '자세히 보기' 버튼을 img Link 태그 안에 넣어서 이미지 클릭시 이동하는 src가 변경될때 같이 변경되게 하였다.

 

 

4. 인디케이터로 생성

일단 먼저 가지고 있는 배너 데이터의 개수만큼 dot을 불러오기 위해 map을 이용하고 위에서 만든 currentIndex 와 현재 dot의 index가 같다면 'active' 클래스를 추가해 미리 만들어 놓은 css를 연결해주어 활성화된 인디케이터를 시각적으로 구별 가능하게 해주었다. hadleSlide 함수는 사용자가 인디케이터를 클릭했을때 활성화된 슬라이드를 변경하는 역할을 해준다.

 

5. 일정 시간이 지나면 자동으로 다음 이미지로 넘어감

useEffect 훅을 사용하여 컴포넌트가 마운트된 후에 주기적으로 타이머를 실행하도록 했다. 타이머는 setInterval을 사용하여 일정시간마다 실행되고 만약 마지막 장일때는 currentIndex를 0으로 되돌력 첫 장으로 돌아가게 하였다.

   useEffect(() => {
        const interval = setInterval(() => {
            setCurrentIndex((count) => count === carouselDate.length - 1 ? 0 : count + 1)
        }, 3000)

        return () => {
            clearInterval(interval);
            };
    },[carouselDate])

 


생각보다 오류 없이 잘 끝낼수 있었던 Carousel 이었다! 추후에 리펙토링 단계에서 인터렉션 적인 요소를 추가해 넣을 예정이다..!

'dev > code review' 카테고리의 다른 글

[React] 무한 스크롤 기능 구현  (0) 2023.06.04
[HTML, CSS] portfolio 페이지 만들기  (0) 2023.03.26
[HTML, CSS] login modal 만들기  (0) 2023.03.25

댓글