배너는 어떻게 개발하나요?
평소처럼 웹 서비스를 이용하는데 문득 '메인 페이지의 배너는 어떻게 개발하는 걸까' 궁금증이 들었어요.
이번 글에서는 제가 배너를 개발한 방식에 대해 얘기해볼게요.
배너를 개발하기 위해서 먼저 다음 두가지에 대해 생각해볼 필요가 있었습니다.
- 여러개의 배너 이미지 중 어떻게 하나의 배너 이미지만 노출시킬 것인가?
- 배너 이미지를 자동으로 변경하려면 어떻게 해야 하는가?
1. 여러개의 배너 이미지 중 어떻게 하나의 배너 이미지만 노출시킬 것인가?
이를 위해서 'position: absolute'를 사용해서 배너 이미지들을 같은 지점에 위치시켰습니다. 그리고 'display: none', 'display: block'을 사용해서 하나의 배너 이미지만 노출시키고 다른 배너 이미지들은 노출되지 않도록 했습니다.
Banner.tsx
...
const bannerImage = ['https://.../1.jpg', 'https://.../2.jpg', 'https://.../3.jpg']; // 이미지 url을 갖고있는 배열
const [bannerIndex, setBannerIndex] = useState<number>(0); //화면에 보여지는 배너 이미지의 인덱스를 갖는 state
...
{bannerImage.map((image, index) => {
return (
<div
className={`banner__image${
bannerIndex === index ? " active" : ""
}`}
key={index}
>
<a href="" style={{ background: `url(${image})` }}></a>
</div>
);
})}
...
}
Banner.scss
...
.banner__image {
position: absolute;
width: 100%;
height: 100%;
display: none;
animation: bannerOpacity 0.7s;
a {
display: block;
height: 100%;
}
.active {
display: block;
}
}
@keyframes bannerOpacity {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
...
사용자에게 노출시킬 배너 이미지를 정하기 위해서 bannerIndex라는 state를 사용했습니다. 배너 이미지의 index가 bannerIndex와 동일할 경우 className에 active를 추가해서 'display: block'이 적용되도록 했습니다.
2. 배너 이미지를 자동으로 변경하려면 어떻게 해야 하는가?
배너 이미지를 자동으로 변경하기 위해서 setInterval을 사용했습니다.
주의할 점은 react에서는 setInterval을 다음과 같은 코드만으로 사용할 경우
setInterval(() => setBannerIndex((bannerIndex + 1) % bannerImage.length), 3000);
랜더링이 될 때 마다 새로운 interval이 생성되기 때문에 문제가 발생합니다. 그래서 unmount가 될 때 clearInterval을 호출해서 현재 실행 중인 interval을 제거해야 합니다. 이를 위해서 useEffect를 사용했습니다.
useEffect(() => {
const id = setInterval(() => setBannerIndex((bannerIndex + 1) % bannerImage.length), 3000)
return () => clearInterval(id);
}, [bannerIndex]);
브라우저에서는 다음과 같이 보여집니다.
전체 코드는 다음과 같습니다.
Banner.tsx
import * as React from "react";
import { useState, useEffect } from "react";
import "../styles/Banner.scss";
function Banner() {
const bannerImage = [
"https://edit-edition.com/images/m-1.jpg",
"https://edit-edition.com/web/upload/NNEditor/20211110/03_shop1_201805.jpg",
"https://edit-edition.com/web/upload/NNEditor/20211110/12_shop1_201806.jpg",
];
const [bannerIndex, setBannerIndex] = useState<number>(0);
const [bannerChangeFlag, setBannerChangeFlag] = useState<boolean>(true);
const handleLeftBtnClick = (e: React.MouseEvent) => {
setBannerIndex(
bannerIndex === 0
? bannerImage.length - 1
: (bannerIndex - 1) % bannerImage.length,
);
};
const handleRightBtnClick = (e: React.MouseEvent) => {
setBannerIndex((bannerIndex + 1) % bannerImage.length);
};
const handleDotClick = (e: React.MouseEvent) => {
const index = e.currentTarget.getAttribute("data-index");
setBannerIndex(index ? Number(index) : 0);
};
const handleMouseEnter = (e: React.MouseEvent) => {
setBannerChangeFlag(false);
};
const handleMouseLeave = (e: React.MouseEvent) => {
setBannerChangeFlag(true);
};
useEffect(() => {
let id: NodeJS.Timer;
if (bannerChangeFlag) {
id = setInterval(
() => setBannerIndex((bannerIndex + 1) % bannerImage.length),
3000,
);
}
return () => clearInterval(id);
}, [bannerIndex, bannerChangeFlag]);
return (
<div className="banner">
{bannerImage.map((image, index) => {
return (
<div
className={`banner__image${
bannerIndex === index ? " active" : ""
}`}
key={index}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<a href="" style={{ background: `url(${image})` }}></a>
</div>
);
})}
<div className="banner__leftBtn" onClick={handleLeftBtnClick}></div>
<div className="banner__rightBtn" onClick={handleRightBtnClick}></div>
<ul className="banner__dots">
{bannerImage.map((image, index) => {
return (
<li key={index}>
<button
className={bannerIndex === index ? "active" : undefined}
data-index={index}
onClick={handleDotClick}
></button>
</li>
);
})}
</ul>
</div>
);
}
export default Banner;
Banner.scss
.banner {
position: relative;
height: 300px;
min-width: 1200px;
padding-left: 70px;
.banner__image {
position: absolute;
width: 100%;
height: 100%;
display: none;
animation: bannerOpacity 0.7s;
a {
display: block;
height: 100%;
}
}
.active {
display: block;
}
.banner__leftBtn {
position: absolute;
top: 135px;
left: 150px;
cursor: pointer;
}
.banner__leftBtn:after {
content: "";
width: 20px;
height: 20px;
border-top: 5px solid #d7d7d7;
border-right: 5px solid #d7d7d7;
display: inline-block;
transform: rotate(225deg);
transition: 0.2s;
}
.banner__leftBtn:hover:after {
border-top: 5px solid #000;
border-right: 5px solid #000;
}
.banner__rightBtn {
position: absolute;
top: 135px;
right: 80px;
cursor: pointer;
}
.banner__rightBtn:after {
content: "";
width: 20px;
height: 20px;
border-top: 5px solid #d7d7d7;
border-right: 5px solid #d7d7d7;
display: inline-block;
transform: rotate(45deg);
transition: 0.2s;
}
.banner__rightBtn:hover:after {
border-top: 5px solid #000;
border-right: 5px solid #000;
}
.banner__dots {
position: absolute;
left: 0;
right: 0;
bottom: 10px;
text-align: center;
padding-left: 70px;
li {
display: inline-block;
margin: 0px 3px;
button {
height: 16px;
border-radius: 8px;
cursor: pointer;
background-color: #d7d7d7;
border-color: #d7d7d7;
}
.active {
background-color: #000;
border-color: #000;
}
}
}
}
@keyframes bannerOpacity {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Author And Source
이 문제에 관하여(배너는 어떻게 개발하나요?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jj_24/배너는-어떻게-개발하나요저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)