3 분 소요

TIL

과제 전형 진행

구현

드래그 앤 드롭

Drag and Drop Event

dragstart 사용자가 객체(object)를 드래그하려고 시작할 때 발생함.
dragenter 마우스가 대상 객체의 위로 처음 진입할 때 발생함.
dragover 드래그하면서 마우스가 대상 객체의 위에 자리 잡고 있을 때 발생함.
drag 대상 객체를 드래그하면서 마우스를 움직일 때 발생함.
drop 드래그가 끝나서 드래그하던 객체를 놓는 장소에 위치한 객체에서 발생함.
dragleave 드래그가 끝나서 마우스가 대상 객체의 위에서 벗어날 때 발생함.
dragend 대상 객체를 드래그하다가 마우스 버튼을 놓는 순간 발생함.

DataTransfer 객체

드래그 앤 드롭 이벤트를 위한 모든 이벤트 리스너 메소드(event listener method)는 DataTransfer 객체를 반환합니다.

이렇게 반환된 DataTransfer 객체는 드래그 앤 드롭 동작에 관한 정보를 가지고 있게 됩니다.

draggable

옮겨질 요소를 모두 draggable로 설정한다.

// Favorite.jsx (부모 컴포넌트)
// return
  savedItems.map((movie, index) => (
    <MovieCard
      draggable
      onDragOver={handleDragOver}
      onDragStart={(e) => handleDragStart(e, index)}
      onDragEnd={handleDragEnd}
      onDragEnter={(e) => handleDragEnter(e, index)}
      onDrop={handleDrop}
      key={movie.imdbID}
      movieInfo={movie}
    />

// MovieCard.jsx (자식 컴포넌트)
const MovieCard = ({ movieInfo, ...rest }) => {
  // 생략
  return (
    <S.CardContainer onClick={openModal} {...rest}>
      {/* 생략 */}
    </S.CardContainer>
  );
};

draggable 뿐만 아니라 다른 drag 관련 이벤트 속성들도 바로 적용될 수 있도록 rest 파라미터를 이용했다.

움직임

DragStart

drag을 시작했을 때 잡은 요소의 위치를 set하고,
dataTransfer.effectAllowed를 move로 바꿔준다.

  • dataTransfer.effectAllowed

    The DataTransfer.effectAllowed property specifies the effect that is allowed for a drag operation. The copy operation is used to indicate that the data being dragged will be copied from its present location to the drop location. The move operation is used to indicate that the data being dragged will be moved, and the link operation is used to indicate that some form of relationship or connection will be created between the source and drop locations.
    

    드랙 연산 시에 사용되는 효과이고, copy로 설정하면 옮겨질 자리에 복사가 될 것 이라는 것을 알려주고, move는 옮겨질 것을 알려주고, link는 옮겨질 자리에 링크가 생길 것을 알려준다고 한다.

    dragStart에 설정해주어야 한다고 mdn에 나와있다.

DragOver

e.preventDefault로 ms 단위 동작을 막아준다.

handleDragEnter

잡은 요소가 옮겨질 대상 객체 위로 올라갔을 때 동작하는데,
이때 옮겨질 대상 객체의 position을 가지고와서 targetPosition에 set해줄 것이다.

DragEnd / Drop

이제 잡은 요소를 놓았을 때 동작처리를 할 것이다.
이때 기존 요소를 변경하여 옮겨진대로 배열을 재구성/sorting해야 한다.

  • Dragend는 어디에서든 일단 drag을 놓았을 때 무조건 동작한다.

  • Drop은 적절한 곳에 놓았을 경우에만 동작한다.
    그래서 다른 요소와 겹쳐져서 놓았을 경우에만 동작하게 된다.

일단 다른 곳에 놓았을 때 동작할 필요는 없다고 생각이 되어서 (어차피 1열로 세운 리스트이고 순서만 바꾸면 되니) drop 메서드를 이용하였다.

그 안에서 위치 선정대로 배열을 바꾸어주었다.

const handleDragOver = (e) => {
  e.preventDefault();
};

const handleDragStart = (e, index) => {
  grabPosition.current = index;
  e.dataTransfer.effectAllowed = "move";
};

const handleDragEnter = (e, index) => {
  targetPosition.current = index;
};

const handleDrop = () => {
  const copiedItems = [...savedItems];

  copiedItems.splice(grabPosition.current, 1);
  copiedItems.splice(
    targetPosition.current,
    0,
    savedItems[grabPosition.current]
  );

  setSavedItems(copiedItems);
};

위치 선정

옮기는 요소랑 옮겨질 자리 바꾸기

const handleDrop = () => {
  let copiedItems = [...savedItems];

  copiedItems[grabPosition] = copiedItems.splice(
    targetPosition,
    1,
    copiedItems[grabPosition]
  )[0];

  setSavedItems(copiedItems);
};

일단 splice는 (시작지점, 삭제길이, 집어넣을 요소)이고 삭제된 요소를 반환한다.
그래서 위 동작은 옮겨질 자리(targetPosition)에 있는 요소 1개를 삭제하고, 잡고있는 요소(copiedItems의 grabPosition번째 요소)를 넣는다.
그러면 옮겨질 자리에 있던 요소가 반환이 되고, 그 반환된 요소를 잡고있던 요소의 원래 자리에 넣어준다.

이렇게 되면 잡은 요소와 옮겨진 자리의 요소의 위치가 서로 뒤바뀌게 된다.!

하지만, 이렇게가 아닌 옮겨질 자리 사이에 끼워넣고 싶었기 때문에 다음과 같이 수정했다.

옮기는 요소를 옮겨질 자리에 끼워넣기

const handleDrop = () => {
  const copiedItems = [...savedItems];

  copiedItems.splice(grabPosition.current, 1);
  copiedItems.splice(
    targetPosition.current,
    0,
    savedItems[grabPosition.current]
  );

  setSavedItems(copiedItems);
};

리팩토링

position number

grabPosition과 targetPosition을 useState로 사용했었으나
position을 정할 때 setState를 써서 rendering을 일으키지 않아도 되므로 useRef로 대체했다.

  • useState

    const [grabPosition, setGrabPosition] = useState(null);
    const [targetPosition, setTargetPosition] = useState(null);
    
    const handleDragStart = (e, index) => {
      setGrabPosition(index);
      e.dataTransfer.effectAllowed = "move";
      e.dataTransfer.setData("text/html", e.target);
    };
    
    const handleDragEnter = (e, index) => {
      setTargetPosition(index);
    };
    
    const handleDrop = () => {
      const copiedItems = [...savedItems];
    
      copiedItems.splice(grabPosition, 1);
      copiedItems.splice(targetPosition, 0, savedItems[grabPosition]);
    
      setSavedItems(copiedItems);
    };
    
  • useRef

    const grabPosition = useRef();
    const targetPosition = useRef();
    
    const handleDragStart = (e, index) => {
      grabPosition.current = index;
      e.dataTransfer.effectAllowed = "move";
      e.dataTransfer.setData("text/html", e.target);
    };
    
    const handleDragEnter = (e, index) => {
      targetPosition.current = index;
    };
    
    const handleDrop = () => {
      const copiedItems = [...savedItems];
    
      copiedItems.splice(grabPosition.current, 1);
      copiedItems.splice(
        targetPosition.current,
        0,
        savedItems[grabPosition.current]
      );
    
      setSavedItems(copiedItems);
    };
    

회고 (TIL)

2022.03.29 Daily 회고

✏오늘 한 일

  • 과제 전형 마무리
    • 드래그 앤 드롭
    • 배포
    • 리드미 작성
  • 과제 전형 두개 제출

⁉느낀 점

drag and drop 여기저기 참고하면 어떻게 구현은 금방한 것 같다.
그런데 drag 관련 이벤트만해도 7개이고, dataTransfer 객체도 오늘 처음 알게 되었다.
이 부분은 지속적인 공부가 필요한 부분이라고 생각된다.

과제 전형이 생각보다 간단해서 불안하다. 간단해서 구현하는 데에 문제가 없었다는 것이 아니라,
다른 사람은 잘 할 것 같은데 나만 헤매일 것 같은 그런 난이도..?

아직 실력이 많이 부족하다는 것을 많이 느낀다.
대신 많이많이 해봐야겠다고 생각했다.

🎃현재 나의 상태

취업했다고 끝은 아닐 것이다.
시작일 거라고 생각하니 조금 두렵긴 하지만,
다 그렇게 하잖아! 나도 할 수 있다.


댓글남기기