Swiper - Navigation, Pagination Custom

iskkiri2024년 11월 01일
React
Swiper
Swiper - Navigation, Pagination Custom

Swiper는 반응형 웹 개발에서 많이 사용되는 슬라이더 라이브러리로, 특히 사용하기 쉬운 NavigationPagination 기능을 제공합니다. 이번 포스팅에서는 React Swiper에서 기본 내장 기능을 사용하면서도, 이를 스타일링하여 커스텀하는 방법을 다뤄보겠습니다.

 

1. CSS 클래스 기반 스타일링

 

먼저, Swiper의 내장 로직을 그대로 사용하면서 CSS 클래스만 수정하는 방법을 살펴보겠습니다.

 

Swiper의 네비게이션은 기본적으로 .swiper-button-prev.swiper-button-next라는 클래스명을 가지고 있습니다. 이 클래스를 활용해 CSS를 추가하여 네비게이션 버튼을 커스터마이징할 수 있습니다. 예를 들어, 아이콘을 변경하려면 background-image 속성을 활용하면 됩니다.

 

.swiper-button-next {
  background: url("이미지링크") no-repeat center;
  					.
  					.
  					.
}

 

 

문제점

 

 

 

Swiper의 네비게이션은 기본적으로 Swiper 컨테이너 내부에 생성됩니다. 그리고 Swiper 컨테이너에는 기본적으로 overflow: hidden 속성이 적용되어 있기 때문에 버튼을 Swiper 컨테이너 외부로 이동하면 보이지 않게 됩니다. 또한, background-image를 사용해 아이콘을 변경하면 아이콘 색상 커스터마이징이 제한적일 수 있습니다.

 

2. Swiper 인스턴스와 상태값을 이용한 커스터마이징

 

다음은 Swiper 인스턴스에 접근하여 Navigation과 Pagination UI를 Swiper 컨테이너 외부에서 사용할 수 있는 방법입니다. 이를 통해 좀 더 자유로운 커스터마이징이 가능합니다.

 

import "swiper/css";
import "./styles.css";

import { useCallback, useRef, useState } from "react";
import Swiper from "swiper";
import { Swiper as ReactSwiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination } from "swiper/modules";
import { ChevronLeft, ChevronRight } from "react-feather";

export default function App() {
  const swiperRef = useRef<Swiper | null>(null);
  const [activeIndex, setActiveIndex] = useState(0);

  const onSlideChange = useCallback((swiper: Swiper) => {
    setActiveIndex(swiper.realIndex);
  }, []);

  const onPrevSlide = useCallback(() => {
    swiperRef.current?.swiper.slidePrev();
  }, []);
  
  const onNextSlide = useCallback(() => {
    swiperRef.current?.swiper.slideNext();
  }, []);

  return (
    <div className="block">
      <div className="navigation-pagination">
        <button onClick={onPrevSlide}>
          <ChevronLeft color="#666" size={20} />
        </button>

        <div className="swiper-pagination-custom">{activeIndex + 1} / 5</div>

        <button onClick={onNextSlide}>
          <ChevronRight color="#666" size={20} />
        </button>
      </div>

      <ReactSwiper
        ref={swiperRef}
        onSlideChange={onSlideChange}
      >
        {[...Array(5)].map((_, i) => (
          <SwiperSlide key={i}>Slide {i + 1}</SwiperSlide>
        ))}
      </ReactSwiper>
    </div>
  );
}

 

문제점

 

이 방식은 Swiper의 내장 모듈이 아닌  Navigation과 Pagination을 직접 구현해야 합니다. 예를 들어, 첫 슬라이드에서 “이전” 버튼을 비활성화하려면 isBeginning 상태와 같은 추가적인 상태값을 관리해야 합니다.

 

const [isBeginning, setIsBeginning] = useState(true);

const onReachBeginning = useCallback((swiper: Swiper) => {
  setIsBeginning(swiper.isBeginning);
}, []);

 

자체적으로 기능을 정의하거나, 간단한 로직을 정의할 경우에는 유용할 수 있지만, Swiper 내장 모듈의 기능을 그대로 사용할 경우 선언해야하는 상태값이 많아져 로직이 복잡해질 수 있습니다.

 

3. Custom 클래스를 이용한 Navigation, Pagination 커스터마이징

 

 

마지막으로 Swiper의 Navigation 및 Pagination 모듈 기능을 유지하면서 UI를 자유롭게 정의하는 방법입니다. 이 방법은 Navigation과 Pagination에 custom 클래스명을 정의하고, UI에 직접 스타일을 적용합니다.

 

import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import "./styles.css";

import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination } from "swiper/modules";
import { ChevronLeft, ChevronRight } from "react-feather";

export default function App() {
  return (
    <div className="block">
      <div className="navigation-pagination">
        <button className="swiper-button-prev-custom">
          <ChevronLeft color="#666" size={20} />
        </button>

        <div className="swiper-pagination-custom" />

        <button className="swiper-button-next-custom">
          <ChevronRight color="#666" size={20} />
        </button>
      </div>

      <Swiper
        navigation={{
          prevEl: ".swiper-button-prev-custom",
          nextEl: ".swiper-button-next-custom",
        }}
        pagination={{
          el: ".swiper-pagination-custom",
          type: "fraction",
        }}
        modules={[Navigation, Pagination]}
      >
        {[...Array(5)].map((_, i) => (
          <SwiperSlide key={i}>Slide {i + 1}</SwiperSlide>
        ))}
      </Swiper>
    </div>
  );
}

 

  • Navigation: prevEl과 nextEl에 커스텀 클래스 이름을 정의하고, 같은 클래스 이름을 HTML 태그에 적용하여 Swiper와 연결합니다.

     

  • Pagination: el에 커스텀 클래스 이름을 정의하고, 같은 클래스 이름을 HTML 태그에 적용하여 Swiper와 연결합니다.

 

동작 원리

 

Swiper 레퍼지토리의 Navigation 모듈의 코드를 보면 동작 원리를 이해할 수 있습니다.

 

let nextEl = getEl(params.nextEl);
let prevEl = getEl(params.prevEl);
Object.assign(swiper.navigation, {
  nextEl,
  prevEl,
});

 

위 코드에서 params.nextElparams.prevElnavigation prop 객체에 설정한 커스텀 클래스명을 나타냅니다. Swiper는 이 클래스명을 이용해 슬라이더가 탐색 버튼으로 사용할 요소를 찾고, swiper.navigation 객체에 연결합니다.

 

getEl 함수는 지정된 클래스명을 기반으로 Swiper가 탐색 버튼을 찾는 함수입니다. 함수는 다음과 같이 정의되어 있습니다.

 

function getEl(el) {
  let res;
  if (el && typeof el === 'string' && swiper.isElement) {
    res = swiper.el.querySelector(el) || swiper.hostEl.querySelector(el);
    if (res) return res;
  }
  if (el) {
    if (typeof el === 'string') res = [...document.querySelectorAll(el)];
    if (
      swiper.params.uniqueNavElements &&
      typeof el === 'string' &&
      res &&
      res.length > 1 &&
      swiper.el.querySelectorAll(el).length === 1
    ) {
      res = swiper.el.querySelector(el);
    } else if (res && res.length === 1) {
      res = res[0];
    }
  }
  if (el && !res) return el;
  // if (Array.isArray(res) && res.length === 1) res = res[0];
  return res;
}

 

여기서 el 매개변수는 문자열 클래스명을 받으며, Swiper는 이 클래스명을 통해 요소를 검색하여 Navigation 버튼으로 설정할 요소를 찾습니다.

 

  1. 첫 번째 조건문: swiper.isElementtrue일 때, Swiper가 Shadow DOM이나 웹 컴포넌트 내에서 사용 중인지를 확인하고, 그렇지 않다면 다음 조건으로 넘어갑니다.

  2. 두 번째 조건문: 이 부분에서 el이 문자열 클래스명일 경우 document.querySelectorAll(el)을 사용해 Swiper 컨테이너 외부에서 해당 클래스명을 가진 요소들을 검색합니다. 따라서, Swiper 컨테이너 외부에 정의된 커스텀 버튼 요소를 연결할 수 있게 됩니다.

  3. uniqueNavElements 처리: swiper.params.uniqueNavElements 옵션이 활성화되어 있다면 Swiper는 중복된 요소 중 유일한 Navigation 요소를 사용하려고 시도합니다. 이는 커스텀 요소가 여러 개일 때 단일 요소만 선택하여 혼동을 방지하는 역할을 합니다.

 

이렇게 getEl 함수는 커스텀 네비게이션 클래스를 이용해 Swiper 외부 요소를 탐색하고, 해당 요소를 swiper.navigation.nextElswiper.navigation.prevEl에 연결하여 Swiper 컨테이너 외부에서도 탐색 버튼을 작동시킬 수 있게 합니다.

 

이런 동작 원리를 이해하면, Swiper의 기본 기능을 유지하면서도 React Swiper의 navigationpagination을 원하는 디자인 요소로 커스텀하여 사용할 수 있습니다.

Swiper - Navigation, Pagination Custom