| name | swiper |
| description | Swiper 14.x 슬라이더/캐러셀 — React 컴포넌트 + Swiper Element, 핵심 모듈, 반응형, 성능 최적화, Next.js SSR 패턴 |
Swiper 14.x 슬라이더/캐러셀
소스: https://swiperjs.com/react, https://swiperjs.com/swiper-api, https://swiperjs.com/element, https://swiperjs.com/blog/swiper-v14, https://swiperjs.com/blog/swiper-v12, https://swiperjs.com/changelog
검증일: 2026-08-11
버전: Swiper 14.1.0 (2026-08-06 릴리즈) 기준 — v14.0.0은 2026-06-26 릴리즈. v13은 건너뛰고 v12 → v14로 이동
설치
npm install swiper
npm install swiper@12
브라우저 baseline (v14 breaking change)
v14는 최근 약 2년의 에버그린 브라우저로 지원 범위를 상향했습니다. 레거시 DOM 호환 헬퍼와 baseline 이하 기능 감지 코드가 제거되었습니다.
| 브라우저 | 최소 버전 |
|---|
| Chrome / Edge | 110+ |
| Safari (iOS 포함) | 16.4+ |
| Firefox | 110+ |
지원 대상이 이 범위 밖이면 v14로 올리지 말고 v12에 머무릅니다.
주의: v14 릴리즈 노트의 "Node.js >= 20.19.0" 요구는 로컬 툴체인·빌드 환경 기준입니다(이전 engines 값은 >= 4.7.0). 브라우저 런타임 동작과는 무관합니다.
방식 선택 기준: React 컴포넌트 vs Swiper Element
| 기준 | swiper/react | Swiper Element (Web Component) |
|---|
| React 친화성 | 높음 — JSX props로 바로 설정 | 낮음 — Object.assign + initialize() |
| TypeScript | 자연스러운 타입 추론 (v14에서 타입 정확도 향상) | 별도 JSX 타입 선언 필요 |
| 공식 권장 | React 프로젝트에 적합 | 프레임워크 독립 프로젝트에 적합 |
| SSR | 'use client' 추가로 해결 | 동일 |
| React 19 지원 | 지원 | 지원 |
Swiper Element가 한때 "미래 권장 방식"으로 소개되었으나, v14 기준 swiper/react는 계속 유지·제공됩니다(v14.1.0 공식 문서의 React 페이지·타입 문서 활성 유지). React 프로젝트에서는 swiper/react 사용을 권장합니다.
방식 1: React 컴포넌트 (swiper/react)
기본 설정
'use client';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
function HeroSlider() {
return (
<Swiper
modules={[Navigation, Pagination, Autoplay]}
spaceBetween={20}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
autoplay={{ delay: 3000, disableOnInteraction: false }}
loop
>
<SwiperSlide>
<img src="/slide1.jpg" alt="Slide 1" />
);
}
핵심 모듈 import
import {
Navigation,
Pagination,
Autoplay,
EffectFade,
EffectCoverflow,
Thumbs,
FreeMode,
Virtual,
Keyboard,
Mousewheel,
A11y,
} from 'swiper/modules';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/effect-fade';
import 'swiper/css/effect-coverflow';
import 'swiper/css/thumbs';
import 'swiper/css/free-mode';
v12부터 .scss / .less 소스가 제거되어 CSS 경로만 존재합니다. swiper/scss, swiper/less import는 v12+에서 동작하지 않으므로 전부 swiper/css*로 교체합니다.
TypeScript 타입 패턴
import { Swiper, SwiperSlide } from 'swiper/react';
import type { SwiperRef, SwiperClass, SwiperProps } from 'swiper/react';
import type { SwiperOptions } from 'swiper/types';
const swiperRef = useRef<SwiperRef>(null);
const [swiperInstance, setSwiperInstance] = useState<SwiperClass | null>(null);
<Swiper
ref={swiperRef}
onSwiper={setSwiperInstance}
>
{/* ... */}
</Swiper>
swiperInstance?.slideNext();
swiperInstance?.slideTo(2);
swiperInstance?.autoplay.start();
swiperInstance?.autoplay.stop();
const swiperOptions: = {
: ,
: ,
: ,
};
v14는 타입 선언을 런타임 소스에서 직접 생성(tsc)하도록 바꿔 타입이 구현과 어긋날 여지를 없앴습니다. 대신 기존에 any 캐스팅으로 Swiper 내부에 접근하던 코드에서 새 타입 에러가 드러날 수 있습니다. 컴파일 타임 문제일 뿐 런타임 동작 변화는 없습니다.
이벤트 핸들링
<Swiper
onSwiper={(swiper) => setSwiperInstance(swiper)}
onSlideChange={(swiper) => {
console.log('active index:', swiper.activeIndex);
}}
onSlideChangeTransitionEnd={(swiper) => {
}}
onReachEnd={() => {
}}
onTouchStart={(swiper, event) => {
}}
onTouchEnd={(swiper, event) => {
}}
onProgress={(swiper, progress) => {
}}
>
useSwiper / useSwiperSlide 훅
import { useSwiper, useSwiperSlide } from 'swiper/react';
function SlideNavButton() {
const swiper = useSwiper();
return <button onClick={() => swiper.slideNext()}>Next</button>;
}
function SlideContent() {
const slideData = useSwiperSlide();
return <div className={slideData.isActive ? 'active' : ''}>...</div>;
}
반응형 breakpoints
<Swiper
slidesPerView={1}
spaceBetween={10}
breakpoints={{
640: {
slidesPerView: 2,
spaceBetween: 20,
},
768: {
slidesPerView: 3,
spaceBetween: 30,
},
1024: {
slidesPerView: 4,
spaceBetween: 40,
},
}}
>
부분 노출 슬라이드 정렬 — snapToSlideEdge
<Swiper
slidesPerView={1.2}
spaceBetween={16}
snapToSlideEdge
>
커스텀 네비게이션
import { useRef, useState } from 'react';
import type { SwiperClass } from 'swiper/react';
function CustomNavSlider() {
const [swiperInstance, setSwiperInstance] = useState<SwiperClass | null>(null);
const [isBeginning, setIsBeginning] = useState(true);
const [isEnd, setIsEnd] = useState(false);
return (
<div className="slider-wrapper">
<Swiper
onSwiper={setSwiperInstance}
onSlideChange={(swiper) => {
setIsBeginning(swiper.isBeginning);
setIsEnd(swiper.isEnd);
}}
>
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<SwiperSlide>Slide 3</SwiperSlide>
</Swiper>
<button
onClick={() => swiperInstance?.slidePrev()}
disabled={isBeginning}
aria-label="이전 슬라이드"
>
Prev
</>
swiperInstance?.slideNext()}
disabled={isEnd}
aria-label="다음 슬라이드"
>
Next
);
}
기본 네비게이션 화살표 아이콘 (v12+ SVG)
<Swiper
modules={[Navigation]}
navigation={{ addIcons: false }}
>
.swiper {
--swiper-navigation-size: 44px;
--swiper-navigation-top-offset: 50%;
--swiper-navigation-sides-offset: 10px;
--swiper-navigation-color: var(--swiper-theme-color);
}
커스텀 페이지네이션
<Swiper
modules={[Pagination]}
pagination={{
clickable: true,
renderBullet: (index, className) => {
return `<span class="${className}" aria-label="${index + 1}번 슬라이드">${index + 1}</span>`;
},
}}
>
Thumbs (썸네일 갤러리)
import { useState } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { FreeMode, Thumbs } from 'swiper/modules';
import type { SwiperClass } from 'swiper/react';
import 'swiper/css';
import 'swiper/css/free-mode';
import 'swiper/css/thumbs';
function GallerySlider() {
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperClass | null>(null);
return (
<>
{/* 메인 슬라이더 */}
<Swiper
modules={[FreeMode, Thumbs]}
// destroyed 체크 필수 — React StrictMode에서 이중 마운트 시 이전 인스턴스 무효화
thumbs={{ swiper: thumbsSwiper && !thumbsSwiper.destroyed ? }}
=
>
{/* 썸네일 슬라이더 */}
);
}
EffectFade / EffectCoverflow
<Swiper
modules={[EffectFade, Navigation]}
effect="fade"
fadeEffect={{ crossFade: true }}
navigation
>
<Swiper
modules={[EffectCoverflow, Pagination]}
effect="coverflow"
coverflowEffect={{
rotate: 50,
stretch: 0,
depth: 100,
modifier: 1,
slideShadows: true,
}}
pagination
centeredSlides
>
방식 2: Swiper Element (Web Component)
프레임워크 독립적 Web Component 방식. React 프로젝트에서는 JSX 타입 선언이 별도로 필요합니다.
기본 설정 (React)
'use client';
import { useEffect, useRef } from 'react';
import { register } from 'swiper/element/bundle';
register();
function ElementSlider() {
const swiperRef = useRef<HTMLElement>(null);
useEffect(() => {
const swiperEl = swiperRef.current;
if (!swiperEl) return;
const params = {
slidesPerView: 1,
navigation: true,
pagination: { clickable: true },
breakpoints: {
640: { slidesPerView: 2 },
1024: { slidesPerView: 3 },
},
};
Object.assign(swiperEl, params);
(swiperEl as any).initialize();
}, []);
return (
<swiper-container ref={swiperRef} init="false">
<swiper-slide>Slide 1</swiper-slide>
Slide 2
Slide 3
);
}
단순 설정은 kebab-case 속성으로도 지정 가능합니다 (slides-per-view="3", grid-rows="3"). breakpoints처럼 중첩 객체가 필요하면 위의 init="false" + Object.assign + initialize() 패턴을 사용합니다. 초기화된 인스턴스는 컨테이너 엘리먼트의 swiper 프로퍼티로 접근합니다.
TypeScript 타입 선언 (Swiper Element)
declare namespace JSX {
interface IntrinsicElements {
'swiper-container': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
init?: boolean | string;
navigation?: boolean | string;
pagination?: boolean | string;
'slides-per-view'?: number | string;
'space-between'?: number | string;
loop?: boolean | string;
},
HTMLElement
>;
'swiper-slide': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement>,
HTMLElement
>;
}
}
주의: Swiper Element에서 이벤트 이름은 소문자 + swiper 접두사입니다 (slideChange → swiperslidechange). 이벤트 데이터는 event.detail로 전달되며, 접두사는 events-prefix 속성(eventsPrefix 파라미터)으로 변경합니다.
마이그레이션 노트 (v11 → v14)
v13은 릴리즈되지 않았습니다(v12 → v14). 코드 변경이 실제로 필요한 지점은 대부분 v12 단계에 몰려 있고, v14는 브라우저·툴체인 baseline과 타입 엄격도만 달라집니다.
| 버전 | 릴리즈 | 코드 영향 |
|---|
| 12.0.0 | 2025-09-11 | SCSS/LESS 소스 제거(CSS-only), 네비게이션 아이콘 폰트 → 인라인 SVG, virtual slidesPerViewAutoSlideSize 추가 |
| 12.1.0 | 2026-01-28 | snapToSlideEdge 추가, 키보드 이동 speed 지정 지원 |
| 14.0.0 | 2026-06-26 | TypeScript 전면 재작성, 브라우저 baseline 상향, ssr-window 의존성 제거(런타임 의존성 0), 번들 2~4% 축소 |
| 14.1.0 | 2026-08-06 | 버그 수정 (모듈 기본값 처리, 뷰포트 기준 오프셋, breakpoints 타입 보존) |
체크리스트
- SCSS/LESS import 교체 (v12) —
import 'swiper/scss' → import 'swiper/css'. swiper/scss/navigation 등 모듈 경로도 swiper/css/navigation으로.
- SCSS 변수 기반 테마 → CSS 커스텀 프로퍼티 (v12) —
--swiper-theme-color, --swiper-navigation-* 등으로 오버라이드.
- 네비게이션 화살표 스타일 재확인 (v12) — 아이콘이 SVG로 바뀌었으므로
.swiper-button-next::after { font-size: ... } 같은 폰트 기반 커스텀은 더 이상 동작하지 않습니다. --swiper-navigation-size로 크기를 조정하거나 navigation={{ addIcons: false }}로 끄고 직접 마크업을 넣습니다.
- 브라우저 지원 범위 확인 (v14) — Chrome/Edge 110+, Safari 16.4+, Firefox 110+ 밖이면 v12 유지.
- 타입 체크 재실행 (v14) —
any로 Swiper 내부에 접근하던 코드에서 새 타입 에러가 나올 수 있습니다.
- loop 옵션 확인 (v11부터) —
loopedSlides는 v11에서 제거됨. loopAdditionalSlides를 사용합니다.
v12 → v14 업그레이드 자체는 옵션·기본값·이벤트·페이로드·메서드 시그니처·모듈 import가 모두 그대로이므로 코드 변경이 필요 없습니다.
상세 레퍼런스 (예제·고급 패턴·흔한 실수) → references/REFERENCE.md