여행 가이드 프로젝트를 진행하며 Swiper를 사용하게 되었다. Next.js 14 앱 라우터에서의 Swiper 사용법은 크게 어렵지 않다. 이번 포스트에서는 Swiper를 학습하고 사용한 내용을 기록하고 공유한다. 천천히 따라온다면 쉽게 Swiper를 다룰 수 있게 될 것이다.
npm install swiper
Next.js와 같은 React 기반 프로젝트에서는 Swiper를 React 컴포넌트로 사용해야한다. 그래서 swiper/react에서 Swiper 컴포넌트를 가져와야 한다.
import Swiper from 'swiper/react';
Swiper의 모듈은 슬라이더의 특정 기능을 제공하는 독립적인 코드 블록이다. 모듈을 사용하면 Swiper의 기본 기능을 확장하여 다양한 기능(예: 네비게이션, 페이지네이션, 스크롤바 등)을 추가할 수 있다. 필요에 따라 모듈을 선택적으로 가져와 사용할 수 있다.
import { FreeMode, Mousewheel, Navigation, Pagination } from "swiper/modules";
이외에도 많은 특징을 가진 모듈이 존재한다. 자세한 내용은 최하단 참고문헌의 공식문서 링크에서 확인 가능하다.
Swiper가 제공하는 스타일을 적용하기 위해서 Swiper를 사용할 때 CSS 파일을 가져와야 한다. Swiper의 슬라이더와 관련된 레이아웃, 애니메이션, 버튼 스타일 등의 기본적인 스타일링이 이 CSS 파일들에 포함되어 있다. 따라서 Swiper를 사용할 때 필요한 모듈과 함께 해당 모듈의 스타일을 가져와야 슬라이더가 올바르게 표시되고 동작한다.
⬇️ Navigation, Pagination 모듈만을 사용했을 경우의 swiper css import
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
위에서 설명한 방법은 필요한 모듈만을 가져오고, css도 모듈에 맞게 가져와서 사용하는 일반적인 방법이다. 이렇게 번거롭게 하나하나 가져오지 않고도 번들을 사용하면 단 한 번 import만으로 모든 모듈과 css를 사용할 수 있다.
하지만 모든 모듈이 포함되어 있어 파일 크기가 커지고, 이는 초기 로딩 시간에 영향을 줄 수 있게 된다. 또한 프로젝트에서 사용하지 않는 모듈도 포함되어 있어 불필요한 코드가 로드될 수 있다. 이는 최적화 측면에서 바람직하지 않다.
Swiper의 다양한 기능을 테스트하거나 빠르게 설정하고 싶다면 번들 버전을 사용하는 것도 좋은 방법이다.
import Swiper from 'swiper/bundle';
import 'swiper/css/bundle';
<Swiper>
와 <SwiperSlide>
는 함께 사용되어 슬라이더를 구성한다. Swiper는 전체 슬라이더의 컨테이너로서, 슬라이더의 설정과 기능을 제어한다. 반면에 SwiperSlide는 슬라이더의 각 슬라이드를 정의하여, 슬라이더가 어떤 콘텐츠를 포함할지를 결정한다.
<Swiper>
<Swiper>
는 Swiper 슬라이더의 기본 컨테이너 컴포넌트이다. 이 컴포넌트는 슬라이더의 설정과 모듈을 정의하고, 슬라이더의 전체 구조를 담당한다. Swiper 컴포넌트는 슬라이더를 초기화하고, 슬라이드 전환, 네비게이션, 페이지네이션 등의 다양한 기능을 제공하는 역할을 한다.
<Swiper
modules={[Navigation, Pagination]}
spaceBetween={50}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
>
{/* SwiperSlide 태그가 여기에 들어온다. */}
</Swiper>
<SwiperSlide>
<SwiperSlide>
는 각 슬라이드를 정의하는 컴포넌트이다. Swiper 컨테이너 내에 포함되며, 각 슬라이드의 콘텐츠를 정의한다. SwiperSlide는 슬라이더의 개별 슬라이드를 나타내며, 여러 개의 SwiperSlide를 추가하여 슬라이더를 구성할 수 있다.
<SwiperSlide>
<div>Slide 1 Content</div>
</SwiperSlide>
<SwiperSlide>
<div>Slide 2 Content</div>
</SwiperSlide>
<SwiperSlide>
<div>Slide 3 Content</div>
</SwiperSlide>
이번 프로젝트에서 사용했던 Carousel 코드 2개를 공유한다. 위의 설명을 참고하며 예시 코드를 천천히 확인해 보면 어떻게 Swiper를 사용해야 하는지 쉽게 감을 익힐 수 있을 것이다.
🚨 예시 코드가 길기도 하고, 외주 프로젝트이기에 모든 부분을 공개할 수 없어 Swiper 관련 코드만 기록한다.
"use client";
import Image from "next/image";
import { useState } from "react";
import { FreeMode, Mousewheel, Navigation, Thumbs } from "swiper/modules";
import { Swiper, SwiperClass, SwiperSlide } from "swiper/react";
import "swiper/swiper-bundle.css";
export default function Carousel({
//
}) {
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperClass | null>(null);
const [currentSlide, setCurrentSlide] = useState(1);
const allImages = [
...images.map((image) => image.image),
...g_reviews.flatMap((review) =>
review.review_images.map((image) => image.image)
),
...reviews.flatMap((review) =>
review.review_images.map((image) => image.image)
),
];
const handleSlideChange = (swiper: SwiperClass) => {
setCurrentSlide(swiper.activeIndex + 1);
};
return (
<div>
<Swiper
modules={[Navigation, Thumbs]}
navigation={{
nextEl: ".swiper-button-next-custom",
prevEl: ".swiper-button-prev-custom",
}}
thumbs={{
swiper:
thumbsSwiper && !thumbsSwiper.destroyed ? thumbsSwiper : null,
}}
className="h-[250px]"
onSlideChange={handleSlideChange}
>
{allImages.map((image, index) => (
<SwiperSlide key={index} className="h-full">
<div className="w-full h-full relative">
<Image
src={image}
alt={`${title} 이미지`}
fill
sizes="100%"
className="object-cover rounded-lg"
/>
</div>
</SwiperSlide>
))}
<div className="swiper-button-next-custom absolute top-1/2 right-4 transform -translate-y-1/2 z-10 cursor-pointer">
<Image
src=""
width={40}
height={40}
alt="Next"
/>
</div>
<div className="swiper-button-prev-custom absolute top-1/2 left-4 transform -translate-y-1/2 z-10 cursor-pointer">
<Image
src=""
width={40}
height={40}
alt="Prev"
/>
</div>
<div className="absolute top-4 right-4 text-white text-sm font-normal z-10 bg-black bg-opacity-70 rounded-3xl px-[10px] py-[5px]">
{`${currentSlide} / ${allImages.length}`}
</Swiper>
</div>
<Swiper
modules={[Thumbs, FreeMode, Mousewheel]}
onSwiper={setThumbsSwiper}
spaceBetween={12}
slidesPerView="auto"
freeMode={true}
mousewheel={{ forceToAxis: true }}
watchSlidesProgress
className="overflow-x-auto"
>
{allImages.map((image, index) => (
<SwiperSlide key={index} style={{ width: "100px" }}>
<div className="w-[100px] h-[68px] relative">
<Image
src={image}
alt={`${title} 썸네일 이미지`}
fill
sizes="100%"
className="object-cover cursor-pointer rounded-lg"
/>
</div>
</SwiperSlide>
))}
</Swiper>
</div>
);
}
"use client";
import Image from "next/image";
import { FreeMode, Mousewheel, Navigation, Pagination } from "swiper/modules";
import { Swiper, SwiperClass, SwiperSlide } from "swiper/react";
import "swiper/swiper-bundle.css";
import { useState } from "react";
export default function Carousel({
example_tracks,
}) {
const [activeIndex, setActiveIndex] = useState(0);
const handleSlideChange = (swiper: SwiperClass) => {
setActiveIndex(swiper.activeIndex);
};
return (
<Swiper
modules={[Pagination, FreeMode, Mousewheel, Navigation]}
mousewheel={{ forceToAxis: true }}
pagination={{ clickable: true }}
className="w-full"
slidesPerView={2.5}
centeredSlides={true}
spaceBetween={240}
onSlideChange={handleSlideChange}
>
{example_tracks.map((track, index) => (
<SwiperSlide key={track.id}>
<div
className={`flex flex-col items-center h-full gap-[16px] mb-2 transition-all duration-300 ${
index === activeIndex
? "scale-100 opacity-100"
: "scale-75 opacity-50"
}`}
>
<Image
src={track.track_images.resized_image}
alt={track.track_images.content}
fill
className="object-cover"
/>
<Image
src=""
alt="track play button"
width={76}
height={76}
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
/>
<h3 className="text-title3 pb-[30px] text-grayscale-9 text-center w-[250px] truncate mt-6">
SWIPER 테스트
</h3>
</div>
</SwiperSlide>
))}
</Swiper>
);
};
Next.js 14에서 Swiper를 사용할 때 몇 가지 주의사항이 있다.
use client
를 사용하여 컴포넌트가 클라이언트 측에서 실행되도록 설정해야 한다.과거에 바닐라 자바스크립트로 캐러셀을 구현했던 경험이 있다. 기본적인 캐러셀을 구현하는 것은 어렵지 않았지만, 여전히 고려해야 할 사항이 많고 구현하는 데 시간이 꽤 걸렸다. 특히, 이번 외주 프로젝트에서는 클라이언트가 인디케이터(페이지네이션)를 포함한 캐러셀을 요구했는데, 이를 바닐라 자바스크립트로 구현하려고 하니 작업이 단순하지 않을 것으로 예상 됐다.
외주 프로젝트는 마감 데드라인이 정해져 있었고, Swiper를 사용하면 이러한 복잡한 요구사항도 매우 간단하게 해결할 수 있을 것 같아 선택하게 되었다. 실제로 Swiper를 사용하니 예상했던 것보다 훨씬 빠르고 쉽게 캐러셀을 구현할 수 있었다.