⛑주의!!! swiper 메이저버전이 10으로 올라갔습니다.
(2023.07.31 기준)
메이저 버전이 10으로 올라감에 따라 아래 제 포스트를 따라했을 때 잘 동작하지 않을 수 있습니다.
본 포스트의 swiper 버전은 ^9.3.2 입니다
😙라이브러리의 도움을 받아 캐러셀 구현하기
캐러셀 같은 경우 처음 코딩을 시작했을 때 구현을 시도했던 기능인데
그당시에는 자바스크립트만 이용해서 이래저래 구글링하면서 해결했던 기억이 납니다.
지금 간단히 기억나는 느낌으로는 overflow되는 것들을 보이지않게 처리하고
일정주기 혹은 클릭이벤트가 발생할때마다 보여지는 이미지의 위치를 전환하는 식으로 구현했던 것 같습니다.
다만 이번 프로젝트에서 필요로하는 캐러셀은 요구되는 기능이 많았습니다.
따라서 이 기능들을 하나하나 구현하기에는 시간적인 여유가 없을 것 같았습니다.
따라서 라이브러리 사용을 고려하게 되었고 그 이전에 제 프로젝트 세팅을 설명하면
TypeScript, Next.js13 , npm , tailwind css를 사용하고 있습니다.
이런 캐러셀 기능을 위한 라이브러리는 여러가지가 있을 수 있지만
가장 많이들 사용하는 라이브러리는 swiper와 react/slick 정도일 것 같습니다.
😙시작하기전에 먼저 완성본부터 보고 가요!
제가 작성한 코드를 잘 따라오신 뒤엔 다음과 같이 브라우저의 크기에 따라
표시되는 캐러셀의 수를 변하게할 수 있습니다!
다만 저는 css에서 시행착오를 많이 거쳐서 살짝살짝 어색한 부분이 있네요..
이부분은 CSS로 캐러셀의 크기를 잘 조절해주시면 될 것 같습니다.
😃react-slick vs swiper
npm 패키지의 다운로드 순위를 보여주는 사이트 npm trend를 참고해보면 다음과 같습니다.
물론 swiper는 리액트에 국한된 라이브러리가 아니니
개인적인 생각으로는 리액트 생태계에선 비슷비슷한 지위를 가지고 있지 않을까 예상해봅니다.
저는 swiper를 선택했는데 그 이유는 카카오 웹툰 이었던 것 같은데
어떤 기업 기술 블로그에서 swiper를 사용하여 겪은 경험을 공유한 게시물을 본 기억이 있어
swiper가 좀 더 친근하게 느껴졌기 때문에 택하게 되었습니다.
🤮swiper/react 사용법 익혀보기 전에 체크해보고 가기
swiper의 docs를 참고해보면 사용법을 쉽게 익힐 수 있습니다.
리액트 사용자들을 배려한 것인지 swiper/react가 따로 있네요
반응 구성 요소는 번역기가 react를 반응으로 번역해버려서 저렇게 되어버린 것 같습니다.
읽어보면 향후 지원되지 않을 가능성이 높으니 마이그레이션을 추천합니다.
아무래도 이미 코드를 짜놓은게 아니라면 추천하는 대로 마이그레이션을 하는 것도 좋을 것 같습니다.
기존에 사용해 본 적이 있는 것도 아니니 러닝커브는 비슷할 것 같으니까요
하지만 조금 치명적인 문제가 있어보였습니다.
React doesn't fully supports web components yet (as for version 18). So the usage is basically the same as in HTML:
아쉽지만 현재 기준으로 리액트 버전 18의 경우 web components가 완전히 지원되지 않는다고 합니다.
어떤 부분에서 문제가 되는지는 모르겠지만 열심히 짜놨는데 호환이 안되면 두번 일하게 될테니
swiper/react를 사용하기로 결정했습니다.
🤑설치방법
npm i swiper
우선 swiper를 설치해줍니다. swiper/react 역시 swiper에 들어있으니 걱정하지 않아도 됩니다.
😷사용방법
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';
// Import Swiper styles
import 'swiper/css';
export default () => {
return (
<Swiper
spaceBetween={50}
slidesPerView={3}
onSlideChange={() => console.log('slide change')}
onSwiper={(swiper) => console.log(swiper)}
>
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<SwiperSlide>Slide 3</SwiperSlide>
<SwiperSlide>Slide 4</SwiperSlide>
...
</Swiper>
);
};
공식문서에서 제공해주는 예제는 다음과 같습니다.
보면 알 수 있듯이 핵심적인 역할을 하는 컴포넌트 두개가 있습니다.
Swiper와 SwiperSlide가 그것입니다.
그리고 다양한 설정들은 Swiper의 props로 넘겨주는 것을 통해 구현하는 형식입니다.
위 설정들을 다양하게 주기만 하면 알아서 구현이 되니 너무 편리하네요
Swiper라는 컨테이너에 내부에 SwiperSlide를 각각 넣어주면 됩니다.
import 'swiper/css';
주의할 것은 기본적인 애니메이션, 스타일을 적용받고 싶다면
위와 같이 css를 import해주어야 한다는 것입니다.
지원해주는 css의 목록은 공식문서에서 확인할 수 있습니다만
굉장히 많은 설정이 있으니 개인적인 추천으로는 Demos 탭에서
마음에 드는 탭을 고르고 수정해보는 방식을 추천드립니다.
import 'swiper/scss';
import 'swiper/scss/navigation';
import 'swiper/scss/pagination';
다들 기본적으로 넣게되는 기능으로는 이정도가 있을 듯 합니다.
이제 위 demos 링크에서 마음에 드는 예제를 고른 뒤
제공해주는 코드 샌드 박스에 접근합니다.
React의 예제를 원한다면 React 를 클릭해주면 코드샌드박스로 이동하게됩니다.
😝pagenation 구현해보기
import React, { useRef, useState } from "react";
// Import Swiper React components
import { Swiper, SwiperSlide } from "swiper/react";
// Import Swiper styles
import "swiper/css";
import "swiper/css/pagination";
import "./styles.css";
// import required modules
import { Pagination } from "swiper";
export default function App() {
return (
<>
<Swiper pagination={true} modules={[Pagination]} className="mySwiper">
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<SwiperSlide>Slide 3</SwiperSlide>
<SwiperSlide>Slide 4</SwiperSlide>
<SwiperSlide>Slide 5</SwiperSlide>
<SwiperSlide>Slide 6</SwiperSlide>
<SwiperSlide>Slide 7</SwiperSlide>
<SwiperSlide>Slide 8</SwiperSlide>
<SwiperSlide>Slide 9</SwiperSlide>
</Swiper>
</>
);
}
Swiper에서 import해온 모듈 Pagination을 배열안의 요소로 담아 전달해주고
pagination에 true를 주면 페이지네이션을 구현할 수 있습니다.
스타일링의 경우 바로 className을 주는것을 통해 적용할 수 있습니다.
그외에도 필요에 따라 Demos를 참고하시면서 진행하면 쉽게 익히실 수 있습니다.
그러니 조금 까다로울 수 있는 breakpoint에 대해서 학습해봅시다.
😴반응형을 위한 breakpoints
브레이크 포인트는 미디어쿼리 역할을 해주는 옵션이라고 생각할 수 있습니다.
const swiper = new Swiper('.swiper', {
// Default parameters
slidesPerView: 1,
spaceBetween: 10,
// Responsive breakpoints
breakpoints: {
// when window width is >= 320px
320: {
slidesPerView: 2,
spaceBetween: 20
},
// when window width is >= 480px
480: {
slidesPerView: 3,
spaceBetween: 30
},
// when window width is >= 640px
640: {
slidesPerView: 4,
spaceBetween: 40
}
}
})
docs를 참고해보면 위와 같은 형태로 작성한다고 합니다만
swiper/react에 대한 예제는 제공해주지 않는건가..?싶네요
결론부터 말하면 리액트에서도 비슷한 형태로 사용하면됩니다.
<Swiper
breakpoints={{
768: {
slidesPerView: 2,
},
}}
>
리액트에서 사용한다면 Swiper 컴포넌트에 props로 넘겨주면 됩니다.
의미는 다음과 같습니다.
breakpoints: {
// when window width is >= 320px
320: {
slidesPerView: 2,
spaceBetween: 20
}
}
window width가 320px 이상일때에는 다음 설정이 적용된다는 뜻입니다.
spacebetween은 말그대로 간격을 의미합니다.
slidePerView는 한 슬라이드에 표시될 캐러셀의 수를 의미합니다.
2라고하면 한번에 2개의 캐러셀이 표시되는 것입니다.
바로 이 breakpoints를 활용하여 반응형으로 동작하는 캐러셀을 작성할 수 있는 것이죠!
breakpoints: {
'@0.75': {
slidesPerView: 2,
spaceBetween: 20,
},
'@1.00': {
slidesPerView: 3,
spaceBetween: 40,
},
'@1.50': {
slidesPerView: 4,
spaceBetween: 50,
},
}
ratio의 형태로 엔드포인트를 잡아주고싶다면 다음과 같은 형태로 작성해주면 됩니다.
😏실전에서 사용해보기
먼저 저는 components/carousel의 구조로
캐러셀과 관련된 파일들을 한곳에 모아 관리하는 식으로 구조를 잡았습니다.
모두가 제 방식을 따를 필요는 없겠지만 이렇게 작성한 이유는
Carousel의 css 파일을 만들어 오버라이딩하는 것을 통해 기본적으로 주어지는 UI를 사용하는 것이 아니라
기본 제공되는 CSS의 핵심적인 부분들은 사용하면서 필요한 부분만 커스터마이징하기 위함입니다.
.swiper-button-prev,
.swiper-button-next {
background-color: #fff;
opacity: 0.6;
height: 50px;
width: 50px;
border-radius: 50px;
color: black !important;
}
.swiper-button-prev:after,
.swiper-button-next:after {
font-size: 1.1rem !important;
font-weight: 600 !important;
}
Carousel.css의 내용은 다음과 같으며
불투명하게 하얀색이며 동그란 페이지 스와이핑 버튼으로 만들어주는 CSS입니다.
import해온 jpg의 type은 어떻게 설정해줘야할까..??
import Gorani from '../../assets/carousel/gorani.jpg';
import Deepdive from '../../assets/carousel/deepdive.jpg';
import { CarouselVariants } from './Carousel';
import { VariantProps } from 'class-variance-authority';
export interface CarouselDataType
extends VariantProps<typeof CarouselVariants> {
imgsrc: typeof Gorani;
subject: string;
label: string;
}
export const CarouselData: CarouselDataType[] = [
다음은 제가 캐러셀에 넘겨줄 데이터의 타입을 정의해주는 부분입니다.
조금 헷갈릴 수 있는 부분이 있었는데
😂리액트에서 import해온 jpg파일의 타입은 무엇일까요?
'StaticImageData' 형식은 'string' 형식에 할당할 수 없습니다.
string 형식에 할당하려고 해보니 StaticImageData형식을 할당할 수 없다고합니다.
import 해온 jpg 파일의 타입은 StaticImageData인거군요!
그런데 StaticImageData를 타입으로 정해주려고하니 또 정의되지 않은 타입이라고 오류를 뱉네요
이런 경우엔 타입스크립트가 확장하여 제공해주는
imgsrc: typeof Gorani;
typeof 를 이용해 해결할 수 있습니다.
https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html
typeof에 대한 내용은 위 docs에서 확인할 수 있습니다.
자바스크립트에서의 typeof와 비슷한 역할을 수행하지만
자바스크립트에는 없는 타입스크립트의 문법으로 정의된 타입들도 인식한다는 특징이 있어요!
이 typeof를 이용하면 편리하게 타입을 작성할 수 있습니다.
import해온 jpg 파일의 식별자에 typeof를 걸어주면 간단하게 문제가 해결되네요
'use client';
import { FC } from 'react';
import { Navigation, Pagination, Autoplay } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';
import { CarouselData } from './CarouselData';
import Carousel from './Carousel';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import './Carousel.css';
interface CarouselContainerProps {}
const CarouselContainer: FC<CarouselContainerProps> = ({}) => {
return (
<Swiper
modules={[Navigation, Pagination, Autoplay]}
rewind={true}
navigation={true}
autoplay={{
delay: 2500,
disableOnInteraction: false,
}}
pagination={{ clickable: true }}
spaceBetween={10}
slidesPerView={1}
className={`
w-[20rem] h-[10rem] md:w-[30rem] md:h-[15rem] lg:w-[61rem] my-6 max-w-[500px] md:max-w-[976px] max-h-[15rem]
`}
breakpoints={{
976: {
slidesPerView: 2,
},
}}
>
{CarouselData.map((carouselItem, idx) => (
<SwiperSlide key={`carousel${idx}`}>
<Carousel
imgsrc={carouselItem.imgsrc}
subject={carouselItem.subject}
label={carouselItem.label}
variant={carouselItem.variant}
/>
</SwiperSlide>
))}
</Swiper>
);
};
export default CarouselContainer;
코드를 하나씩 살펴보면 쉽게 이해할 수 있습니다.
modules={[Navigation, Pagination, Autoplay]}
rewind={true}
navigation={true}
autoplay={{
delay: 2500,
disableOnInteraction: false,
}}
pagination={{ clickable: true }}
spaceBetween={10}
slidesPerView={1}
rewind={true}는 캐러셀의 끝에 도달했을때 맨처음으로 돌아갈것인지를 결정하는 값입니다.
불리언 값을 넣어주면 되고 default는 false인것으로 보입니다.
navigation의 경우에는 modules에 [Navigation]의 형태로
swiper에서 import해온 Navigation 모듈을 넣어준 다음 navigation props에 true를 주면 사용할 수 있습니다.
autoplay역시 AutoPlay모듈을 추가하고 autoplay의 값을 설정해주면 되겠습니다.
slidesPerView는 한번에 표시될 캐러셀의 갯수입니다.
swiper가 제공하는 데모를 참고하며 작성했고 제가 서술하지 않은 다양한 사용방법이 존재하니
공식 docs를 방문하여 살펴보시는 것을 추천드립니다.
CSS 설정시 추천하는 방법
w-[20rem] h-[9.5rem]
md:w-[25vw] md:h-[15rem]
lg:w-[30rem]
transition-all duration-300
p-2 md:pt-4 mx-4
rounded-lg
flex justify-center items-center m-auto
저는 rem을 사용하여 설정을 해보려고했지만 모든 분기를 rem으로 처리하자
캐러셀이 한화면에 두개가 있는 경우 캐러셀끼리의 겹침 현상이 일어났습니다.
따라서 이러한 현상을 해결하기 위해서 vw,vh 단위를 사용하는 것을 조금 더 추천드립니다.
저는 추후 캐러셀의 단위를 vw로 통일할 생각을 하고 있습니다.
읽어주셔서 감사하고 포스트에 오류가 있는 경우 댓글을 남겨주세요!
'css' 카테고리의 다른 글
요즘 핫한 panda css next.js app 라우터에서 시작하기 (0) | 2023.08.02 |
---|---|
빌드타임 css-in-js 이해하기 pandacss와 vanila extract (0) | 2023.08.02 |
mui 개념과 사용 방법 시작하고 sx 프로퍼티 알아보기 (0) | 2023.07.27 |
Radix UI로 Headless UI 맛보기 (1) | 2023.07.21 |
왜 emotion같은 css-in-js 프레임워크들은 ssr이 힘든걸까? (4) | 2023.05.26 |