
위와 같은 윈도우 화면을 구현하는 과정에서, 각 창과 아이콘을 드래그로 이동시킬 수 있는 동작이 필요했다.
drag&drop 기능을 구현해주는 라이브러리로는
을 많이 사용하는데, 나는 단순히 컴포넌트를 이동시키는 동작만 필요했기 때문에 react-draggable을 사용하기로 했다.
npm i react-draggable
기본적으로 typeScript를 지원하기 때문에, 따로 설정할 필요는 없다.
<Draggable
axis="x"
handle=".handle"
defaultPosition={{x: 0, y: 0}}
bounds={left ?: number , top ?: number ,
right ?: number , bottom ?: number}
position={null}
grid={[25, 25]}
scale={1}
onStart={this.handleStart}
onDrag={this.handleDrag}
onStop={this.handleStop}>
많은 커스텀 요소들이 있지만, 프로젝트에서 사용한 요소는 다음과 같다.
handel
컴포넌트 내의 특정 요소를 클릭하고 있을 때에만 드래그 할 수 있도록 제한하고 싶을 때 사용한다.
"%" 나 "vh"는 사용할 수 없고, "px" 단위만 사용 가능하다.
bounds
컴포넌트의 이동 경계를 지정할 때 사용한다.
onDrag : DraggableEventHandler
onStop : DraggableEventHandler
import Draggable from "react-draggable";
import { useRef, useState } from "react";
const MainPage = () => {
const [isDragging, setIsDragging] = useState(false);
const nodeRef = useRef(null);
const handleOnDrag = () => {
setIsDragging(true);
};
const handleStopDrag = () => {
setTimeout(() => {
setIsDragging(false);
}, 100);
};
return (
<IconList>
{data.map((icon) => {
return (
<Draggable
nodeRef={nodeRef}
onDrag={() => handleOnDrag()}
onStop={handleStopDrag}
key={icon.name}
>
<Icon
key={icon.id}
onClick={() => {
if (isDragging) return;
icon.isModal ? onNothingClick() : navigate(icon.path);
}}
ref={nodeRef}
>
<Img src={icon.img} />
<Name>{icon.name}</Name>
</Icon>
</Draggable>
);
})}
</IconList>
)
각 아이콘을 드래그 할 때, 드래그 뿐만 아니라 클릭 이벤트도 같이 동작하는 것을 막기 위하여 드래그 하는 도중에는 isDragging을 true로 전환하고, isDragging을 true일 때엔 if (isDragging) return;을 통해 클릭을 막도록 했다.
그리고 ReactDOM.findDOMNode()가 더이상 사용되지 않기 때문에 <Draggable>을 제대로 동작 시키기 위해서 nodeRef={nodeRef} 와 ref={nodeRef}를 사용한다.
<Draggable
nodeRef={nodeRef}
handle=".top-container"
>
<Container ref={nodeRef}>
<TopContainer className="top-container">
...
</TopContainer>
...
<Container>
</Draggable>
핸들로 사용하고 싶은 요소에 className을 주고, handle="클래스 네임" 을 전달해 해당 부분을 클릭했을 때에만 드래그가 가능하도록 설정했다.