Preview
swiper 기본 세팅 및 무한 루프 기능
swiper.js 설치
npm i swiper --save
서버에서 받아오는 데이터 처럼 .json 형태의 파일을 fetch로 가져와 slide를 map으로 돌리려 합니다.
// swiper_event.json
{
"data": [
{
"img": "https://images.unsplash.com/photo-1661956603025-8310b2e3036d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80",
"url": "https://www.naver.com/"
},
{
"img": "https://images.unsplash.com/photo-1505373877841-8d25f7d46678?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1112&q=80",
"url": "/cookie"
},
{
"img": "https://images.unsplash.com/photo-1472653431158-6364773b2a56?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1169&q=80",
"url": ""
},
{
"img": "https://images.unsplash.com/photo-1507878866276-a947ef722fee?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1171&q=80",
"url": "https://www.naver.com/"
},
{
"img": "https://plus.unsplash.com/premium_photo-1663100894140-930cf3ff227e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1225&q=80",
"url": ""
}
]
}
// SwiperEvent.tsx
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Autoplay } from 'swiper';
import 'swiper/css';
interface Type_SwiperEvent {
img: string;
url: string;
}
export default function SwiperEvent() {
SwiperCore.use([Autoplay]);
const [swiperList, setSwiperList] = useState<Type_SwiperEvent[]>([]);
useEffect(() => {
fetch(`/data/swiper/swiper_event.json`)
.then(res => res.json())
.then(res => {
setSwiperList(res.data);
});
}, []);
return (
<Wrapper>
{swiperList && swiperList.length > 0 ? (
<Swiper
slidesPerView={1}
loop={true}
autoplay={{ delay: 1500, disableOnInteraction: false }}
>
{swiperList.map((event: Type_SwiperEvent, idx: number) => (
<SwiperSlide
key={idx}
onClick={() => {
if (event.url) {
window.location.href = event.url;
}
}}
>
<img src={event.img} alt="img" />
</SwiperSlide>
))}
</Swiper>
) : null}
</Wrapper>
);
}
const Wrapper = styled.div`
.swiper-slide {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.26%;
overflow: hidden;
z-index: 10;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
`;
1. swiper 관련 라이브러리를 import 해줍니다.
* swiper/css를 Import 하지 않으면 세팅을 해줘도 보이지 않으니 꼭꼭 import 하기!
2. <Swiper> 세팅하기
<Swiper
slidesPerView={1}
loop={true}
autoplay={{ delay: 1500, disableOnInteraction: false }} >
<SwiperSlide>
<img src="https://image_1.com" alt="img" />
</SwiperSlide>
<SwiperSlide>
<img src="https://image_2.com" alt="img" />
</SwiperSlide>
<SwiperSlide>
<img src="https://image_3.com" alt="img" />
</SwiperSlide>
</Swiper>
Swiper 태그 안에 SwiperSlide를 넣어 내부에 전체 콘텐츠를 세팅을 해주면 되는데, 이 부분을 map으로 처리해줍니다.
✅ 옵션 설명
slidePerView: 한 장에 보일 이미지/콘텐츠 개수
loop: 루프를 시킬지 말지
autoplay: 보이는 시간 초 설정 / swiper를 클릭했을 때 loop를 멈추지 않고 계속 돌리겠다는 옵션.
📌 SwiperCore.use([Autoplay]);
autoplay가 적용되지 않을 때 상단에 입력해줍니다.
3. 반응형 적용
swiper 내의 css를 수정하고 싶을 때는 상단의 div 안에 해당 class명을 가지고 수정해줄 수 있습니다.
navigate 버튼 커스텀 하기
// 기존 코드에 추가하기
export default function SwiperEvent() {
const swiperRef = useRef<SwiperCore>();
return (
<Wrapper>
{swiperList && swiperList.length > 0 ? (
<>
<Swiper
onBeforeInit={swiper => {
swiperRef.current = swiper;
}}
>
...
</Swiper>
<SwiperModuleWrap>
<SwiperBtnWrap>
<PrevBtn
type="button"
onClick={() => swiperRef.current?.slidePrev()}
>
Prev
</PrevBtn>
<NextBtn
type="button"
onClick={() => swiperRef.current?.slideNext()}
>
Next
</NextBtn>
</SwiperBtnWrap>
</SwiperModuleWrap>
</>
) : null}
</Wrapper>
);
}
먼저 Swiper 태그 외부로 Button을 빼서 마크업 해야 하며 z-index를 사용해야지 커스텀한 버튼을 볼 수 있습니다. (처음 구현했을 때는 버튼이 있는데 안 보여서 한참 찾은 기억이...😫)
버튼을 누르면 swiper 내부의 함수를 호출해서 이전 / 다음 페이지로 넘겨야 하는데 내부 함수를 사용할 수 있는 방법이 useRef을 명시하여 사용하면 됩니다.
📌 이전 / 다음 슬라이드 이동하는 함수
swiperRef.current?.slidePrev()
swiperRef.current?.slideNext()
Pagination 버튼 커스텀 하기
// 기존 코드에 추가하기
export default function SwiperEvent() {
const swiperRef = useRef<SwiperCore>();
const [currentIdx, setCurrentIdx] = useState(0);
return (
<Wrapper>
{swiperList && swiperList.length > 0 ? (
<>
<Swiper
onBeforeInit={swiper => {
swiperRef.current = swiper;
}}
onSlideChange={e => {
setCurrentIdx(e.realIndex + 1);
}}
>
...
</Swiper>
<SwiperModuleWrap>
<SwiperPgWrap>
<CurrentPg>{currentIdx}</CurrentPg>
<TotalPg>/{swiperList.length}</TotalPg>
</SwiperPgWrap>
</SwiperModuleWrap>
</>
) : null}
</Wrapper>
);
}
swiper는 현재 보이고 있는 페이지의 index를 제공해줍니다. 그걸 토대로 현재 몇 번째 이미지가 로드되고 있는지 state를 사용하여, 페이징 네이션을 커스텀해볼 수 있었습니다.
📌 현재 표시되는 index 값 가져오기
<Swiper
onSlideChange={e => {
setCurrentIdx(e.realIndex + 1);
}}
>
Pagination 버튼 커스텀 하기 (모바일 버전)
export default function SwiperEventMobile() {
const swiperRef = useRef<SwiperCore>();
const [currentIdx, setCurrentIdx] = useState(0);
const clickSlide = (idx: number) => {
swiperRef.current?.slideTo(idx + 1);
setCurrentIdx(idx + 1);
};
return (
<Wrapper>
{swiperList && swiperList.length > 0 ? (
<>
<Swiper
onBeforeInit={swiper => {
swiperRef.current = swiper;
}}
onSlideChange={e => {
setCurrentIdx(e.realIndex + 1);
}}
>
...
</Swiper>
<SwiperModuleWrap>
{swiperList.map((event, idx) => (
<SwiperBtn
key={idx}
onClick={() => clickSlide(idx)}
className={idx + 1 === currentIdx ? 'active' : ''}
/>
))}
</SwiperModuleWrap>
</>
) : null}
</Wrapper>
);
}
모바일 버전에서는 현재 페이지를 숫자로 보여주는 것보다 그래픽으로 보여주는 게 더 직관적이었습니다. 위 코드는 저의 동료가 수정하여 구현했는데, 나중에 잊지 않고자 기록하려 합니다.
콘텐츠의 길이만큼 페이지 네이션과, 페이지 이동 기능을 같이 하는 버튼을 map으로 돌려 보여주고, 버튼을 클릭했을 때 해당 인덱스로 이동시키는 함수와 currentIdx state를 업데이트해주면 됩니다.
👩🏻💻 전체 코드 (PC)
👩🏻💻 전체 코드 (Mobile)
🤔 구현해야 하는 시간이 촉박해 swiper 라이브러리를 사용했는데, 버튼이나 페이지 네이션을 커스텀하여 사용하는 부분에서 검색하는데 많은 시간이 들어갔었습니다. (github, 스택오버플로우 최고..!) 많은 사이트들에서 메인에 이벤트들을 나열할 때 사용하고 있는 기능으로 추후에는 라이브러리 사용 없이 직접 구현해봐야겠다 생각했습니다.
'Web > React & Next.js' 카테고리의 다른 글
TypeScript 환경에서 React Query 사용하기 (0) | 2022.11.28 |
---|---|
React | Swiper.js를 사용하여 카드 슬라이드 구현하기 (Slides per view) (0) | 2022.11.25 |
React | 모바일, 데스크톱기기 구분하여 url 리다이렉트 시키기 (0) | 2022.11.24 |
React | javascript 로 받은 form형태 react form으로 변환 하기 (에스크로 인증마크 설정) (0) | 2022.11.21 |
React | 간단하게 public 경로의 파일 (소개서, 템플릿) 다운로드 (0) | 2022.11.18 |