[React] react-draggable에서 Drag and Drop과 click 이벤트를 구분해야하는 상황

박기영·2023년 6월 18일
1

React

목록 보기
31/32

팀 프로젝트 중 화면 상에서 자유롭게 움직일 수 있고, 클릭하면 채팅 모달을 보여줘야하는 컴포넌트가 있었다.
다른 팀원 분께서 react-draggable을 사용해 제작하시던 중,
드래그 앤 드롭이 끝나는 시점에 click 이벤트가 같이 발생해서 애를 먹었는데
이를 해결하는 과정에 재밌는 내용들이 있어 정리하고자 한다.

문제 상황

컴포넌트를 옮기기 위해서는 드래그 앤 드롭을 해야한다.
즉, 컴포넌트 위에서 마우스를 누른 채로 원하는 위치로 끌고 간 뒤, 마우스를 놓는 과정을 거친다.
문제는 마우스를 누르고 놓는 과정이 클릭으로 인식되기 때문에
Drag and Drop 뿐만 아니라 click 이벤트까지 같이 발생한다는 것이다.

해결 방법

해결 방법은 생각보다 매우 간단했다.
드래그 상태를 나타내는 boolean 타입의 state를 하나 만든 뒤,
드래그 중에는 true, 드롭되면 false로 바꿔주는 것이다.

핵심은 클릭을 막는 것!

물론, 저 방법만으로 해결할 수 있는 것은 아니다.
우리 팀이 해결하고자 했던 것은 드롭되는 순간 click 이벤트가 발생하는 것을 막는 것이다.

사실, click 이벤트 자체를 막는 것은 안된다.

하지만 click 이벤트로 인해 트리거되는 함수는 실행을 막을 수 있다!
그 방법은 바로, 드래그 상태가 true인 경우에 클릭 이벤트 핸들러의 실행을 막는 것이다.

아래 코드를 보며 이해해보자.

코드

import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';

// ... //

function FloatingButton() {
  const [isDragging, setIsDragging] = useState(false); // 드래그 상태를 알려주는 state
  const [position, setPosition] = useState({ x: 0, y: 0 });

  // ... //
  
  const handleOpenChatModal = () => {
    if (isDragging) return; // 드래그 중이라면 함수를 실행하지 않는다.
      
    // ... //
  };

  const handleOnDrag = (e: DraggableEvent, data: DraggableData) => {
    setIsDragging(true); // 드래그 중에는 드래그 state를 true로 전환한다.

    setPosition({ x: data.x, y: data.y });
  };
  
  const handleStopDrag = () => {
    // 드롭 후 100ms 후에 드래그 상태를 false로 전환한다.
    setTimeout(() => {
      setIsDragging(false);
    }, 100);
  };

  // ... //

  return (
    <>
	  // ... //
      
      {!hidden && (
        <Draggable
          position={{ x: position.x, y: position.y }}
          onDrag={(e, data) => handleOnDrag(e, data)} // 드래그 시 실행되는 부분
          onStop={handleStopDrag} // 드롭 시 실행되는 부분
        >
          <div className={styles.floatingButtonContainer}>
            <button
              className={styles.floatingButton}
              onClick={handleOpenChatModal} // 클릭 시 실행되는 부분
              onTouchEnd={handleOpenChatModal} // 모바일에서는 클릭이 안되므로 touch 이벤트를 사용한다.
            >
              <div className={styles.chatIcon}>
                <FloatingChat />
              </div>
            </button>
          </div>
        </Draggable>
      )}
    </>
  );
}

export default FloatingButton;

앞서 설명했던 클릭 이벤트 핸들러의 실행을 막는 것.
그 방법은 드래그 상태를 나타내는 statetrue인 경우에 함수 실행을 중단하는 것이었다.

그 방법의 가장 핵심이 바로 이 코드이다.

const handleStopDrag = () => {
  setTimeout(() => {
    setIsDragging(false);
  }, 100);
};

드롭 이벤트가 발생한 후 100ms 동안 드래그 중 상태를 유지하게 만든다!
이로 인해서 클릭 이벤트 핸들러가 드래그 상태인 것으로 인식해서 함수가 중지된다.
100ms 후 드래그 상태가 false로 바뀌면, 클릭 이벤트는 정상적으로 작동할 수 있게된다.

결과

참고 동영상

드래그 앤 드롭과 클릭이 의도한대로 동작하는 것을 확인할 수 있다.

참고 자료

react-draggable github issue
stackoverflow 질문글 1
stackoverflow 질문글 2

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글