2024. 3. 4. 12:27ㆍreact

처음에는 react-slider 라이브러리를 사용해서 슬라이드를 만들었다. 그런데 사용자 친구에게 포트폴리오를 보여주니
바로 드래그를 해버리는 게 아닌가...?
그래서 라이브러리를 drag-slide가 자연스럽도록 수정을 하려고 했는데 타 블로그에서 react로 drag-slide를 제작하는 글을 보게 되었다.
마우스 드래그로 좌우 스크롤 구현 (ft. React)
아이템들을 행으로 나열하고 설정해놓은 width를 넘어갈 때 overflow-x: scroll로 스크롤을 만들어 보일 수 있게 구현했습니다. 이때 스크롤 바를 움직이는 게 아닌 모바일 환경에서 옆으로 밀어서 이
velog.io
참고 블로그 : https://velog.io/@yezee/React-drag-slider
<React> drag slider
마켓컬리를 클론하면서 느낀거는 마켓컬리는 캐러셀과 슬라이더 천국이다 👼🏻넘치는 패기로 라이브러리 없이 구현해보고 싶었다 하지만 슬라이더의 벽은 높았고... 친절한 다른 블로그를 보
velog.io
솔직히 지금까지 라이브러리를 너무 애용한 사람이라 이벤트를 직접 구현한 글을 보니 나도 도전 정신이 생겼다.
위 블로그 글을 참고해서 내 컴포넌트에도 적용해보도록...!
1. 컴포넌트 생성
: drag-slide 기능을 구현할 컴포넌트를 먼저 생성해준다.
(이때, teamImages는 외부에서 가져온 데이터)
const DragSlide = ({teamImages}) => {
return(
<div>
{teamImages && (
<div className="DragSlide">
{teamImages.map((images) => (
<div className="dragImg" key={images.id}>
<img src={`img/${images.img}.png`} alt={images.name} />
</div>
))}
</div>
)}
</div>
);
};
export default DragSlide;
.DragSlide{
display: flex;
overflow-x: scroll;
}
.DragSlide::-webkit-scrollbar{
display: none;
}
2. useRef 사용해 DOM에 직접 접근하기
블로그 설명에 따르면, 좌우 슬라이드 스크롤의 움직임은 해당 DOM의 scrollLeft로 움직인다고 한다.
(클릭한 시점부터 클릭을 뗀 시점까지)
그래서 해당 DOM의 scrollLeft를 얻기 위해 useRef를 사용해 접근한다고 함.
const DragSlide = ({teamImages}) => {
const scrollRef = useRef();
return(
<div>
{teamImages && (
<div className="DragSlide" ref={scrollRef}>
{teamImages.map((images) => (
<div className="dragImg" key={images.id}>
<img src={`img/${images.img}.png`} alt={images.name} />
</div>
))}
</div>
)}
</div>
);
};
3. 코드 구현
- 사용된 이벤트
onMouseDown : 마우스 왼쪽 버튼 누르고 있는 상태
onMouseUp : 마우스 왼쪽 버튼 뗀 상태
onMouseMove : 마우스를 움직이는 상태(클릭 여부 상관 X)
onMouseLeave : DOM에서 마우스가 벗어났는 지 체크하는 이벤트
- 사용된 변수
DOM.scrollWidth : 스크롤 할 수 있는 총 길이
DOM.clientWidth : 설정한 max width(화면에 보이는 스크롤 길이)
DOM.scrollLeft : 스크롤 가장 왼쪽(DOM.scrollLeft = 0)부터 이동한 스크롤 길이 = DOM.scrollLeft길이만큼 스크롤 이동
mouseEvent.pageX : onMouseDown시 x좌표
import { useRef } from "react";
import "../App.css";
import { useState } from "react";
const DragSlide = ({teamImages}) => {
const scrollRef = useRef(null);
const [isDrag, setIsDrag] = useState(false);
const [startX, setStartX] = useState();
// onDragStart
// startX는 현재 클릭한 pageX와 움직인 스크롤 길이 scrollLeft를 합친 값이다.
// 스크롤이 이동하지 않았을 때는 문제가 없으나 스크롤이 이동된 상태에서 클릭을 한다면
// 브라우저의 width의 pageX 값이 설정돼 순간적으로 앞쪽으로 스크롤됨
// 이를 막기 위해 scrollLeft를 더해 현재 x의 위치를 계산
const onDragStart = (e) => {
e.preventDefault();
setIsDrag(true);
setStartX(e.pageX + scrollRef.current.scrollLeft);
};
// onDragEnd
// onMouseUp, onMouseLeave 이벤트 발생 시 isDrag를 false로 설정
const onDragEnd = () => {
setIsDrag(false);
};
const throttle = (func, ms) => {
let throttled = false;
return(...args) => {
if(!throttled){
throttled = true;
setTimeout(()=>{
func(...args);
throttled = false;
}, ms);
}
};
};
const onDragMove = (e) => {
if(isDrag){
const {scrollWidth, clientWidth, scrollLeft} = scrollRef.current;
scrollRef.current.scrollLeft = startX - e.pageX;
if(scrollLeft === 0){
setStartX(e.pageX);
} else if(scrollWidth <= clientWidth + scrollLeft){
setStartX(e.pageX + scrollLeft);
}
}
};
const delay = 100;
const onThrottleDragMove = throttle(onDragMove, delay);
return(
<div>
{teamImages && (
<div
className="DragSlide"
onMouseDown={onDragStart}
onMouseUp={onDragEnd}
onMouseLeave={onDragEnd}
// onMouseMove
// isDrag변수가 true일 때 발생하도록 설정
// scrollLeft의 값을 설정: 처음 클릭한 x의 좌표 startX에서 움직이면서 변하는
// e.pageX를 빼서 이동한 스크롤 길이를 scrollLeft에 삽입
onMouseMove={isDrag ? onThrottleDragMove : null}
ref={scrollRef}
>
<div className="dragImgContainer">
{teamImages.map((images) => (
<div className="dragImg" key={images.id}>
<img src={`img/${images.img}.png`} alt={images.name} />
</div>
))}
{teamImages.map((images) => (
<div className="dragImg" key={images.id}>
<img src={`img/${images.img}.png`} alt={images.name} />
</div>
))}
</div>
</div>
)}
</div>
);
};
export default DragSlide;
4. 코드 리팩토링하기
평상 시 이벤트 제거
onMouseMove이벤트는 클릭 여부 상관없이 해당 DOM에 위치 시 발생함
이를 막기 위해 isDrag가 false일 때 작동하지 않도록 설정
isDrag ? onThrottleDragMove : null
return(
···
<div
className="DragSlide"
onMouseDown={onDragStart}
onMouseUp={onDragEnd}
onMouseLeave={onDragEnd}
onMouseMove={isDrag ? onThrottleDragMove : null}
ref={scrollRef}
>
···
)
너무 많은 이벤트 발생
onMouseMove로 수많은 이벤트가 발생
이벤트를 delay시켜 끊어주는 Throttle을 사용해 해결
const delay = 50; // delay 속도는 본인 마음대로 설정하기 원글에서는 100ms 사용함
const onThrottleDragMove = throttle(onDragMove, delay);
5. 마무리

제작을 하면서 지금까지 내가 라이브러리에 많이 기대고 있었구나 하는 생각이 많이 들었다.
블로그 원글을 살펴보면 알 수 있겠지만 코드를 완성하고 문제점을 파악 후 수정하고 그걸 기록하는 과정이
우리에게는 고작 블로그 한페이지 정도 분량으로 보인다.
하지만 오류를 찾고 수정하는 과정이 그리 쉬운 길이 아니다...
나 또한 6개월간 수업을 들으면서 시간이 없다는 핑계로 라이브러리에 많이 기댄 게 아닌가 싶었다. 자체적으로 코드를 짜볼 생각을 안 했던 것 같다. 물론 고작 6개월 수업 들은 걸로 완벽한 코드가 나오지 않을 것을 알고 있기 때문에 더더욱 손을 대지 않았던 것이겠지만...
라이브러리가 편하게 사용이 가능하기 때문에 라이브러리에 많이 기댄 것도 맞는 것 같다.
그래도 개발자로서 성장을 위해서라면 원글의 개발자님처럼 직접 코드를 짜려는 시도, 완성하려는 노력을 한번이라도 시도해봐야겠다.
지금은 포트폴리오를 수정하면서 지원서를 여기저기 넣고 있지만 서류에서 탈락...탈락... 그래도 어딘가에서는 나를 꼭 필요로 할 거라고 믿는다. 물론 잘난 거 하나 없지만 어딘가는 나의 성장 욕구를 알아줄 곳이...있을 것이라...믿..는다...ㅠ
'react' 카테고리의 다른 글
| react - 무한 롤링 슬라이드 (0) | 2024.03.05 |
|---|---|
| react - hook(useMemo함수, useCallback함수 : 컴포넌트 최적화) (0) | 2024.01.16 |
| react-hook(useRef) (0) | 2024.01.16 |
| react-hook (useState, useEffect) (0) | 2024.01.16 |