[React] 칸반보드를 만들며 배운 것들(beautiful-dnd 사용법)

sjoleee·2022년 8월 20일
1
post-custom-banner

분명 시작할땐 트렐로 클론코딩이었는데... 이것저것 하다보니 내 맘대로 만들어버린 칸반보드를 통해 배운 것들을 정리하고자 한다.
사실 것들 이라기엔... 이리저리 머리 굴린거 + beautiful dnd 사용법 익힌게 전부인 것 같다 ㅋㅋ

https://trello-clone-lake-six.vercel.app/
https://github.com/sjoleee/trello-clone

beautiful-dnd의 기본적인 사용방법

간단하게 적자면,

  • DragDropContext로 드래그, 드롭할 전체 영역을 감싼다.
  • Droppable로 드롭할 영역을 감싼다.
  • Draggable로 드래그할 영역을 감싼다.
  • 드래그가 끝나면 작동할 onDragEnd 함수를 만들어서 DragDropContext에 연결한다.

하나씩 들여다보자.


DragDropContext

DragDropContext는 3개의 prop을 갖는다.

  • onDragStart: drag가 시작될 때 호출되는 함수
  • onDragUpdate: drag 도중에 변화가 생기면 호출되는 함수
  • onDragEnd: drag가 끝날 때 호출되는 함수

이 중에서 onDragEnd만이 필수.

const onDragEnd = () => {};

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div></div>
    </DragDropContext>
  );

또한, DragDropContext은 반드시 자식을 가져야한다.(당연한 거긴 하지..)
대충 빈 함수를 만들어서 넣어줬다.
이 안에 Droppable이 있어야하고, 그 안에 Draggable이 있어야 한다.


Droppable

말 그대로, 뭔가를 드롭할 수 있는 공간을 설정해준다.
Droppable는 droppableId라는 prop을 필수로 가져야한다.
droppableId는 고유한 값이어야함!
그리고 type이라는 prop을 가질 수 있다. 따로 설정해주지 않으면 DEFAULT로 설정되는데, 이 type이 동일한 Droppable과 Draggable이 상호작용 가능하게 된다. drag and drop를 중첩하여 사용하고 싶을 경우, 이 prop을 사용하면 된다.
isDragDisabled은 drag 가능한지 상태를 말한다. 기본으로 false로 가능하게 설정되어있다.

 const onDragEnd = () => {};

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div>
        <Droppable droppableId="testDrop">
          {() => <div></div>}
        </Droppable>
      </div>
    </DragDropContext>
  );

또한, DragDropContext와 마찬가지로 반드시 자식을 가지는데..
이게 뭔가 싶겠지만 함수 형태로 넣어줘야 한다.
왜일까.. latch라는 키워드가 나오던데 궁금하다. 알아봐야겠다.

일단 사용법에 대해 계속 기록하자면,
이 함수는 provided와 snapshot이라는 2개의 인자를 가진다.
두 인자의 type definition을 보자.

export interface DroppableProvided {
    innerRef: (element: HTMLElement | null) => any;
    placeholder?: React.ReactElement<HTMLElement> | null | undefined;
    droppableProps: DroppableProvidedProps;
}

export interface  DroppableStateSnapshot {
    isDraggingOver: boolean;
    draggingOverWith?: DraggableId | undefined;
    draggingFromThisWith?: DraggableId | undefined;
    isUsingPlaceholder: boolean;
}

차근차근 살펴보자.

provided

  • innerRef

droppable이 올바르게 작동하려면, 당신은 반드시 provided.innerRef를 ReactElement의 최상단 DOM 노드에 바인드(bind)해야 합니다. 우리는 ReactDOM를 사용하지 않기 위해 당신의 DOM 노드를 찾을 것입니다.

무슨말이지...? 해석이 필요할 듯 하다 ㅜㅠㅠ
일단, 사용법은 최상단 노드에 바인드하라는 것인데, 이유에 대해 정확히 알고싶다.


  • placeholder

placeholder는 drag and drop중에, droppable의 면적이 변화해야 할 일이 생기게 되면, 해당 부분을 처리해주는 React Element라고 한다.
그러니까, drag 중에 droppable 영역이 축소되지 않도록 해주는 역할이라고 함.

provided.placeholder (?ReactElement) The Draggable element has position: fixed applied to it while it is dragging. The role of the placeholder is to sit in the place that the Draggable was during a drag. It is needed to stop the Droppable list from collapsing when you drag. It is advised to render it as a sibling to the Draggable node. When the library moves to React 16 the placeholder will be removed from api.


  • droppableProps

droppableProps는 droppable로 사용할 컴포넌트에 필요한 props를 모아 놓은 것. 깔끔하게 스프레드 연산자로 처리하는게 일반적이다.

snapshot

  • isDraggingOver
    drag요소가 내 위에 있나요~?로 boolean이다.

  • draggingOverWith
    내 위로 지나가는 drag요소의 ID가 뭐에요?로 draggableId이다.

  • draggingFromThisWith
    여기서 출발하는 drag요소의 ID가 뭐에요?로 draggableId이다.
    출발해서 droppable를 떠나면 null인듯(확인 안해봄)
    스타일할때 사용하면 좋다.

  • isUsingPlaceholder
    provided.placeholder가 사용중인지를 알려준다.


이 두 인자들의 프로퍼티를 잘 활용하면 된다. provided만 필수이며, snapshot은 스타일에 굉장히 유용하게 사용됨.

Draggable

Draggable은 Drag할 수 있는 요소를 감싼다.
Droppable과 마찬가지로 필수로 props를 가지는데, droppableId와 index를 가진다.
index를 가져야 하는 이유는... 드래그해서 자리를 옮기려면 순서라는게 있어야 하기 때문이다.
둘 다 고유한 값이어야 한다.

Droppable과 마찬가지로 함수를 통해 드래그할 컴포넌트를 반환해줘야 함.
함수는 provided와 snapshot를 인자로 갖는다.
두 인자의 type definition을 보자.

export interface DraggableProvided {
    // will be removed after move to react 16
    innerRef: (element?: HTMLElement | null) => any;
    draggableProps: DraggableProvidedDraggableProps;
    dragHandleProps?: DraggableProvidedDragHandleProps | undefined;
}

export interface DraggableStateSnapshot {
    isDragging: boolean;
    isDropAnimating: boolean;
    dropAnimation?: DropAnimation | undefined;
    draggingOver?: DroppableId | undefined;
    // the id of a draggable that you are combining with
    combineWith?: DraggableId | undefined;
    // a combine target is being dragged over by
    combineTargetFor?: DraggableId | undefined;
    // What type of movement is being done: 'FLUID' or 'SNAP'
    mode?: MovementMode | undefined;
}

provided

  • innerRef
    이 역시... 조금 고민해봐야 할 부분인데, DOM 노드를 조회하지 않기 위해서 지정해줘야 하는 필수 프로퍼티라고 한다.
  • draggableProps
    draggableProps는 draggable로 사용할 컴포넌트에 필요한 props를 모아 놓은 것. 깔끔하게 스프레드 연산자로 처리하는게 일반적이다.
  • dragHandleProps
    해당 props를 가진 컴포넌트가 drag에 사용될 손잡이 역할을 하게 된다.
    그러니까... 만약 인풋+텍스트로 구성된 draggable 컴포넌트의 인풋에 dragHandleProps를 넘겨주면 텍스트를 잡고 drag는 불가능해지고 인풋으로만 drag가 가능해진다.

snapshot

  • isDragging
    제가 drag 되고 있나요?

  • isDropAnimating
    dropAnimation이 진행중인가요?

  • dropAnimation
    dropAnimation에 대한 정보

  • draggingOver
    지금 어떤 droppable위를 지나가고 있나요?

combine과 관련된 프로퍼티들은 잘 모르겠다 ㅠㅠ


이 두 인자들의 프로퍼티를 잘 활용하면 된다. provided만 필수이며, snapshot은 스타일에 굉장히 유용하게 사용됨.

간단한 결과물

onDragEnd가 void라서 옮겨지지는 않음

  const onDragEnd = () => {};

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div>
        <Droppable droppableId="testDrop">
          {(provided, snapshot) => (
            <div ref={provided.innerRef} {...provided.droppableProps}>
              <Draggable draggableId="testDrag0" index={0}>
                {(provided) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                  >
                    1</div>
                )}
              </Draggable>
              <Draggable draggableId="testDrag1" index={1}>
                {(provided) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                  >
                    2</div>
                )}
              </Draggable>
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </div>
    </DragDropContext>
  );

profile
상조의 개발일지
post-custom-banner

0개의 댓글