위솝 프로젝트를 하는 동안 외부 라이브러리를 사용할 수 없기에, 배웠던 걸 최대한 활용하여 슬라이드 기능을 구현해보았다. 일단 바로 생각났던 것은 이전에 useState를 처음 배웠을 때 만들어 봤던 카운터! 기본 로직은 아래와 같이 생각했다.
translateX(-(상품의 넓이 * 버튼 클릭 수))
에서 버튼 클릭 수를 변경해주면 원하는 픽셀 만큼 움직일 것이다! const [counter, setCounter] = useState(0);
const leftBtnClickHandler = () => {
setCounter(counter - 1);
};
const rightBtnClickHandler = () => {
setCounter(counter + 1);
};
1) 왼쪽 버튼은 카운터가 0보다 클 때 true가 되도록 만든다.
3) 오른쪽 버튼은 상품의 수가 3보다 크면서, 상품목록 -3이 될 때까지만 true가 되도록 만든다.
(상품의 수가 3개일 때까지는 화면에 보이기 때문에 별도로 버튼이 필요없고, 상품목록 -3 이상으로 버튼을 누를 수 있도록 하면 상품 목록의 끝까지 버튼을 누른 후에도 공백이 더 나오기 때문에 제어해 준다.)
let showLeftBtn = counter > 0;
let showRightBtn = counter !== products.length - 3 && products.length > 3;
<div className="productBodyScrollable">
<div
className="products"
style={{ transform: `translateX(-${27 * counter}%)` }}
>
{products.map(product => {
return <Product key={product.productId} {...product} />;
})}
</div>
{showLeftBtn && (
<div className="carouselLeft">
<button
name="left"
className="carouselLeftBtn"
onClick={leftBtnClickHandler}
>
<i className="fa-solid fa-chevron-left" />
</button>
</div>
)}
{showRightBtn && (
<div className="carouselRight">
<button
name="right"
className="carouselRightBtn"
onClick={rightBtnClickHandler}
>
<i className="fa-solid fa-chevron-right" />
</button>
</div>
)}
</div>
구글링을 해 보니 useRef를 사용한 코드들이 많이 있었다. 그러나 공식문서에 따르면 DOM.focus()등의 접근이 필요한 어쩔 수 없는 경우를 제외하고는 DOM에 직접 접근하는 방식이 좋지 않다고 하여 기존에 사용했던 것들만으로 만들어보긴 했는데 더 좋은 접근 방식이 있는지 동기들의 코드를 살펴보며 참고해봐야 겠다.
추가로, 위 기능을 만들며 가장 시간을 많이 쓴 것은 의외로 CSS였는데, 두 가지 문제가 있었다.
.productBodyScrollable {
position: relative;
width: 78.8%;
height: 100vh;
white-space: nowrap;
overflow: hidden;
역시... 민주님의 로직은 항상 환상적입니다...