제 방을 소개합니다
프로젝트 소개
인테리어 서비스 플랫폼을 가정하고, 컴포넌트 내에서 가구 및 인테리어 용품 정보를 손 쉽게 확인할 수 있는 서비스를 개발해 보았다.
이번 포스팅에서는 해당 개인 프로젝트를 진행하며 발생했던 문제들과, 해당 문제들을 어떻게 해결했는지 정리해보려 한다.
배포 주소
https://house-deco.netlify.app/
🗂 z-index 제대로 짚고 넘어가기
툴팁 뱃지를 클릭했을 때 띄워지는 상품정보 박스가 툴팁뱃지에 가려지는 문제를 발견했다.
그래서 간단히z-index
를 줘서 해결해보려 했지만, 아무리z-index
를 건드려봐도 해결되지 않았다…
문제는 z-index값이 비교되는 방식에 있었다. 나는 단순히 상품정보 박스의 z-index가 높으면 높은대로 제일 상단에 위치할 것이라 생각했었다. 하지만, 나의 컴포넌트 구조는 툴팁 뱃지 아래에 자식으로써 상품정보 박스가 위치해 있었기 때문에, 문제가 발생했던 것이다.
그림으로 설명해보면 아래와 같다.
이렇게 상품정보 박스의 z-index만 높여주면 된다고 생각했던 것이다. 하지만 이 경우에는 아래의 그림과 같은 계산방식이 적용된다.
즉, 결국 상품정보 박스들은 툴팁뱃지의 자식들이기 때문에 아무리 z-index
를 높여줘도 부모를 이길 수 없었던 것이다. 그리고 더 나아가, 부모(툴팁뱃지)들의 z-index
는 모두 0으로 고정되어 있었기 때문에, 그 같은 z-index
값으로 우열을 가릴 수 없으므로 HTML 태그가 배치된 index 순서에 따라 z-index
값이 부여됐던 것이다.
해결 방법
간단히 해결했다.
상품정보 박스를 툴팁뱃지의 자식으로 배치하지 않고 형제 컴포넌트로 배치하니 문제가 해결됐기 때문이다.
그리고 각 상품정보 박스에 조건부 transform: translate(X, Y)
값을 줘서 박스들이 컨테이너 밖으로 돌출되지 않도록 하여 해당 작업을 마무리 했다.
코드
🎨 Border에 Gradient 주기
처음 해보는 작업이라 조금 헤맸다.
그냥 단순하게border
에gradient
값을 주려고 애썼지만, 생각해보니background
를 활용한 트릭을 쓰면 해결될 문제였다.
- 부모
background
색의 디폴트 값에 원하는 색과gradient
값을 준다. - 그리고 제품이 선택됐을 때
img
에border : 2px solid transparent
를 줘서 마치border
가 생긴것 과 같은 트릭을 준다.
코드
border gradient 코드 (60~65번 라인)
🖱️ Drag & Scroll
클릭했을 때 해당 제품의 위치로
translate
을 하는 것은 그리 어렵지 않았지만, 드래그를 통해 제품 리스트를 스크롤되도록 구현하는 부분에서 조금 애를 먹었다.
처음에는 드래그값에 따라 움직여야 하는 distance값에 대한 state
를 주고, 마우스 드래그 거리가 변할 때마다 해당 state
를 업데이트해서 transform
을 할까도 잠시 고민했었다. 하지만 그럴 경우 useRef
를 쓰지 않는 이상 엄청나게 많은 리렌더링이 발생할 것 같아 바로 포기했다.
그러다가 scrollLeft값을 활용해서 이동을 시켜주면 어떨까 라는 생각이 들어 해당 방법을 적용해 보기로 했다.
내가 생각한 로직은 이하와 같다
우선,
mouseDown
이 아닐 때는 이동이 되지 않도록isDown
state
를 생성1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32const [isDown, setIsDown] = useState(false);
export const ProductList = () => {
// 1. isDown state 생성
const [isDown, setIsDown] = useState(false);
// 2. mouseDown이 됐을 때 setIsDown을 true로!
const handleDown = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
setIsDown(true);
};
// 3. 손 떼면 isDown은 false
const handleUp = () => {
setIsDown(false);
};
// 4. 마우스가 움직였어도 isDown이 아니면 return!
const handleMove = (e: React.MouseEvent<HTMLElement>) => {
if (!isDown) return;
e.preventDefault();
};
return (
// 마우스 이벤트에 따라 호출될 함수들 선언
<ListContainer
onMouseLeave={handleUp}
onMouseUp={handleUp}
onMouseDown={(e) => handleDown(e)}
onMouseMove={(e) => handleMove(e)}
>
<ListUl>...생략</ListUl>
</ListContainer>
);
};
mouseDown
&mouseMove
가 될 때 마우스 이동값을 구해 그 만큼 scroll되도록 구현1) 가장 먼저, 첫 클릭된 위치를 기록해야 한다. 이를 위해
startX
라는state
를 만들어준다.1
2const [isDown, setIsDown] = useState(false);
const [startX, setStartX] = useState(0);2) 그리고 상대적 위치를 알아내기 위하여,
useRef
를 통해 DOM에 접근해 스크롤 박스 내에서의 이동값을 계산하기 위한 준비를 한다.1
2
3
4
5
6
7
8
9
10
11
12
13const [isDown, setIsDown] = useState(false);
const [startX, setStartX] = useState(0);
const [scrollLeft, setScrollLeft] = useState(0);
const containerRef: any = useRef();
const handleDown = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
setIsDown(true);
// 우리의 컴포넌트 컨테이너는 중앙에 정렬되어 있기 때문에 offsetLeft값을 빼줘야 스크롤 박스 내에서의 상대적 위치 계산이 가능하다
setStartX(e.pageX - containerRef.current.offsetLeft);
// 그리고 현재 스크롤된 만큼의 거리를 scrollLeft state에 업데이트 해준다.
setScrollLeft(containerRef.current.scrollLeft);
};3) 이제 마우스가 움직였을 때의 로직만 구현하면 된다.
1
2
3
4
5
6
7
8
9
10const handleMove = (e: React.MouseEvent<HTMLElement>) => {
if (!isDown) return;
e.preventDefault();
// mouseMoved에는 말 그대로 움직일 때마다의 마우스의 상대적 위치가 기록된다.
const mouseMoved = e.pageX - containerRef.current.offsetLeft;
// 그리고 toMove에는 위에서 저장된 마우스의 상대적 위치 - 처음 위치를 해주면 된다.
const toMove = mouseMoved - startX;
// 이제 스크롤박스 DOM의 scrollLeft 값을 업데이트 해주면 된다.
containerRef.current.scrollLeft = scrollLeft - toMove;
};