Javascript/React

React Beautiful DnD 라이브러리

kyoulho 2024. 7. 31. 20:23

react-beautiful-dnd는 React 애플리케이션에서 드래그 앤 드롭 기능을 쉽게 구현할 수 있도록 도와주는 라이브러리이다. 이 라이브러리는 복잡한 드래그 앤 드롭 상호작용을 간단하게 처리할 수 있게 설계되었으며, 높은 유연성과 성능을 제공한다.

주요 특징

  1. 쉬운 사용법: API가 간단하고 직관적이며, 드래그 앤 드롭 기능을 쉽게 추가할 수 있다.
  2. 성능 최적화: 가상화와 메모이제이션을 통해 높은 성능을 유지하며, 많은 요소를 포함한 리스트에서도 원활한 성능을 보장한다.
  3. 접근성: 기본적으로 접근성(Accessibility, a11y)을 고려하여 설계되었다.
  4. 애니메이션: 드래그 및 드롭 동작에 대한 부드러운 애니메이션을 제공한다.

설치

npm i --legacy-pee-deps react-beautiful-dnd
npm i -D @types/react-beautiful-dnd

구성 요소

  • DragDropContext: 드래그 앤 드롭 컨텍스트를 설정한다.
  • Droppable: 드롭할 수 있는 영역을 정의한다.
  • Draggable: 드래그 가능한 항목을 정의한다.

 

onDragEnd 콜백 함수와 DropResult

DragDropContext가 요구하는 onDragEnd 콜백 함수 드래그가 끝났을 때 호출되는 함수이다. 이 함수를 구현하려면 DropResult 타입을 이해해야 한다.

// 드래그 앤 드롭 작업의 결과를 나타내는 객체
export interface DropResult {
  // 드래그가 시작된 위치
  source: DraggableLocation;

  // 드래그가 종료된 위치
  // 만약 드래그가 Droppable 외부에서 끝났다면 null
  destination: DraggableLocation | null;

  // 드래그된 항목의 ID
  draggableId: string;

  // 드래그 작업의 타입
  // 여러 종류의 드래그 앤 드롭 작업을 구분하기 위해 사용된다
  // 예를 들어, 여러 개의 드래그 앤 드롭 영역이 있는 경우
  // 각 영역의 타입을 다르게 설정하여 특정 영역 간의 드래그 앤 드롭을 제한할 수 있다
  type: string;

  // 결합된 요소의 목록
  // 드래그 항목이 다른 드래그 항목에 결합된 경우 사용된다
  combine?: Combine;
}

// 드래그 작업의 출발지 또는 목적지를 나타내는 위치 객체
export interface DraggableLocation {
  // Droppable 영역의 ID
  // 이 ID는 드래그된 항목이 드랍된 컨테이너를 식별한다.
  droppableId: string;

  // Droppable 영역 내의 인덱스
  // 드래그된 항목이 해당 Droppable 영역 내에서 위치하는 순서를 나타낸다.
  // 예를 들어, 리스트에서 항목의 순서 또는 위치
  index: number;
}

// 결합된 요소를 나타내는 객체
export interface Combine {
  // 결합된 드래그 항목의 ID
  // 결합된 상태에서 드래그된 항목의 고유 식별자
  draggableId: string;

  // 결합된 Droppable 영역의 ID
  // 드래그 항목이 결합된 Droppable 컨테이너의 식별자
  // 결합은 드래그된 항목이 다른 항목과 함께 결합되는 상황을 나타낸다.
  droppableId: string;
}

 

다중 드롭 영역에서의 드래그 앤 드롭 예제

import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const initialItems = {
  list1: [
    { id: 'item-1', content: 'Item 1' },
    { id: 'item-2', content: 'Item 2' },
  ],
  list2: [
    { id: 'item-3', content: 'Item 3' },
    { id: 'item-4', content: 'Item 4' },
  ],
};

const App = () => {
  const [items, setItems] = useState(initialItems);

  const onDragEnd = (result) => {
    const { source, destination } = result;

    // 드래그가 취소된 경우
    if (!destination) {
      return;
    }

    // 동일한 리스트에서의 이동
    if (source.droppableId === destination.droppableId) {
      const newItems = Array.from(items[source.droppableId]);
      const [movedItem] = newItems.splice(source.index, 1);
      newItems.splice(destination.index, 0, movedItem);

      setItems((prevState) => ({
        ...prevState,
        [source.droppableId]: newItems,
      }));
    } else {
      // 다른 리스트로의 이동
      const sourceItems = Array.from(items[source.droppableId]);
      const destinationItems = Array.from(items[destination.droppableId]);
      const [movedItem] = sourceItems.splice(source.index, 1);
      destinationItems.splice(destination.index, 0, movedItem);

      setItems((prevState) => ({
        ...prevState,
        [source.droppableId]: sourceItems,
        [destination.droppableId]: destinationItems,
      }));
    }
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        {Object.keys(items).map((listId) => (
          <Droppable key={listId} droppableId={listId}>
            {(provided) => (
              <div
                ref={provided.innerRef}
                {...provided.droppableProps}
                style={{
                  background: 'lightgrey',
                  padding: 10,
                  width: 250,
                  minHeight: 500,
                }}
              >
                <h2>{listId}</h2>
                {items[listId].map((item, index) => (
                  <Draggable key={item.id} draggableId={item.id} index={index}>
                    {(provided) => (
                      <div
                        ref={provided.innerRef}
                        {...provided.draggableProps}
                        {...provided.dragHandleProps}
                        style={{
                          padding: 16,
                          margin: '0 0 8px 0',
                          background: 'white',
                          border: '1px solid lightgrey',
                          borderRadius: 4,
                          ...provided.draggableProps.style,
                        }}
                      >
                        {item.content}
                      </div>
                    )}
                  </Draggable>
                ))}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        ))}
      </div>
    </DragDropContext>
  );
};

export default App;

 

'Javascript > React' 카테고리의 다른 글

공개 라우트와 비공개 라우트  (0) 2024.08.01
리액트 라우터  (0) 2024.08.01
React DnD 라이브러리  (0) 2024.07.31
리덕스 미들웨어  (0) 2024.07.29
리듀서 활용하기  (0) 2024.07.22