[React] 라이브러리 없이 무한 슬라이드 만들기
라이브러리를 사용하지 않고 자동 재생 무한 슬라이드 애니메이션을 만들어 보자.
스타일링 라이브러리는 tailwind-CSS를 사용하여 진행한다.
오른쪽에서 왼쪽으로 진행되는 무한 슬라이드를 구현할 데이터 배열에 translateX을 적용할 경우, 배열이 모두 이동하게 된다면 빈 여백을 표시하게 된다.

이를 채워주기 위해서는 해당 배열의 복사본을 연결시켜 무한 슬라이드를 구현하게 된다.
구현 하기에 두 가지 방법을 고안할 수있다.
- 두 개의 배열, 두 개의 애니메이션 : 데이터 배열을 복사본을 만들어, 원본과 복사본의 각각 애니메이션을 적용
- 하나의 배열, 하나의 애니메이션 : 데이터 배열을 원본과 복사본을 연결시켜 하나의 배열로 만들어 하나의 애니메이션을 적용
아래의 이미지에서 슬라이드 데이터는 임의로 6개로 지정한다.
1. 두 개의 배열, 두 개의 애니메이션
원본과 복사본으로 두 개의 배열을 만들고, 각각의 애니메이션을 적용한다.
- 원본 애니메이션: 원본 배열을 처음 보여주고
translateX(-100%)로 이동한다. - 복사본 애니메이션: 복사본은 원본과 복사본의 길이를 이동해야하기 떄문에
translateX(0%)에서translateX(-200%)로 이동한다. - 이 방식은 각 배열이 독립적으로 애니메이션되어 원본이 끝나면 복사본이 시작된다.

tailwind-CSS를 적용하면 아래와 같다.
module.exports = {
theme: {
extend: {
keyframes: {
infiniteSlideOriginal: {
"0%": { transform: "translateX(0)" },
"50%": { transform: "translateX(-100%)" },
"50.1%": { transform: "translateX(100%)" },
"100%": { transform: "translateX(0)" },
},
infiniteSlideCopy: {
"0%": { transform: "translateX(0)" },
"100%": { transform: "translateX(-200%)" },
},
},
animation: {
infiniteSlideOriginal: "infiniteSlideOriginal 20s linear infinite",
infiniteSlideCopy: "infiniteSlideCopy 20s linear infinite",
},
},
},
};
2. 하나의 배열, 하나의 애니메이션
원본 배열과 복사본 배열을 하나로 합쳐 하나의 애니메이션을 적용한다.
이 방식은 단일 애니메이션으로 전체 슬라이드가 이동하며, translateX(-50%)로 이동한다.

module.exports = {
theme: {
extend: {
keyframes: {
infiniteSlide: {
"0%": { transform: "translateX(0)" },
"100%": { transform: "translateX(-50%)" },
},
},
animation: {
infiniteSlide: "infiniteSlide 20s linear infinite",
},
},
},
};
주의점
이미 이미지에서는 일부러 데이터 간의 여백을 표시해두었다.
만약, 데이터 배열내에서 gap을 지정해주었다면 원본+복사본 또는 합쳐진 배열이 한 바퀴가 돌고난 뒤 연결되는 경우에 동일한 여백을 추가해주어야한다.
그렇지 않을 경우, 아래와 같이 데이터 간에 여백이 존재하지 않는 상태로 슬라이드가 연결된다.

각각의 데이터에 스타일링을 적용하거나 배열에서 padding-right를 적용하는 방법이 있다.
아래 코드는 gap과 padding-right를 적용한 하나의 배열, 하나의 애니메이션을 적용한 무한 슬라이드 컴포넌트이다.
export default async function PhotoSlide() {
const { data } = await fetchPixelsPhotos();
const photos = data.photos;
return (
<main className="w-screen h-screen flex justify-center items-center">
<div className="flex w-fit overflow-hidden">
<div className="flex pr-6 gap-6 animate-infiniteSlide">
{[...photos, ...photos].map((photo) => (
<Card
key={photo.id}
description={<p>photo.photographer</p>}
imageSrc={photo.src.large2x}
title={photo.photographer}
/>
))}
</div>
</div>
</main>
);
}
마우스 hover시, 무한 슬라이드 일시 정지
무한히 흘러가는 슬라이드에 사용자가 상호작용을 하거나 데이터를 확인하기 위하여 마우스 hover할 경우에는 멈추는 효과를 추가한다.
tailwind-CSS를 사용하여 스타일링을 적용하며, 이는 다른 스타일링을 사용하거나 순수 CSS을 사용하여도 다른방식을 사용해 동일하게 적용할 수 있다.
1. 슬라이드를 hover 할 경우 해당되는 모든 슬라이드가 일시 정지하게끔 group을 지정한다. (tailwind-CSS group 클래스)
// 두 개의 배열, 두 개의 애니메이션
export default async function PhotoSlide() {
const { data } = await fetchPixelsPhotos();
return (
<main className="w-screen h-screen flex justify-center items-center">
<div className="flex w-fit overflow-hidden group">
<div className="flex pr-6 gap-6 animate-infiniteSlide group-hover">
{data.photos.map((photo) => (
<Card
key={photo.id}
description={<p>photo.photographer</p>}
imageSrc={photo.src.large2x}
title={photo.photographer}
/>
))}
</div>
<div className="flex pr-6 gap-6 animate-afterInfiniteSlide group-hover">
{data.photos.map((photo) => (
<Card
key={photo.id}
description={<p>photo.photographer</p>}
imageSrc={photo.src.large2x}
title={photo.photographer}
/>
))}
</div>
</div>
</main>
);
}
// 하나의 배열, 하나의 애니메이션
export default async function PhotoSlide() {
const { data } = await fetchPixelsPhotos();
const photos = data.photos;
return (
<main className="w-screen h-screen flex justify-center items-center">
<div className="flex w-fit overflow-hidden group">
<div className="flex pr-6 gap-6 animate-infiniteSlide group-hover">
{[...photos, ...photos].map((photo) => (
<Card
key={photo.id}
description={<p>photo.photographer</p>}
imageSrc={photo.src.large2x}
title={photo.photographer}
/>
))}
</div>
</div>
</main>
);
}
2. 애니메이션 일시 정지 애니메이션 추가 및 적용
tailwind-CSS에서는 애니메이션 상태를 일시 정지 시키는 스타일이 존재하지 않기 대문에 CSS를 직접 추가한다.
.group:hover .animate-infiniteSlide {
animation-play-state: paused;
}
.group:hover .animate-afterInfiniteSlide {
animation-play-state: paused;
}

이미지 상에는 마우스 커서가 그렇게 잘 보이진 않는데, 눈 크게 뜨고보면 마우스를 hover 할 때 슬라이드를 일시 정지 시킨다.
완성된 자동 재생 무한 슬라이드

마무리
주의점을 굳이 쓴 이유는 단지, 내가 구현시에 맞딱드린 어려웠던 부분이기 때문이다.
당시에 저부분이 해결되지 않았던 이유는 아주 기본적인 CSS의 이유였다. 박스모델의 width에는 margin이 포함되지 않기때문에 gap을 적용한다음 padding-right를 적용해야하는 것을 잊고 margin-right를 추가하고 있었기 때문이다.
또한, 무한 슬라이드를 구현하기위해 구글링을 많이 해봤더니 무한 슬라이드 일시정지 들을 다양하게 구현하는 것을 확인 할 수 있었다.
순수 CSS로 구현하는 방법이 있는 한편, 자바스크립트로 구현하는 방법도 있었다. 하지만, 자바스크립트로 해당 인터렉션을 관리하게되면 리플로우와 리페인팅이 계속적을 발생된다는 문제점이 발생한다.
순수 CSS로 구현할 수 있는 애니메이션 스타일링이었기 때문에 온전히 GPU에 맡기고 CSS로 무한슬라이드를 구현하는 방법을 선택했다.
- 관련된 코드는 여기서 확인할 수 있다.(Next.js app router 사용) : 레포지토리
레퍼런스
GitHub 계정으로 댓글과 반응을 남길 수 있어요.
댓글을 불러오는 중…