📢 어렵고 정석적인 개념 설명보다는 저같은 초보자도 이해하기 쉽게 정리하는 것을 원칙으로 포스팅하고 있습니다. 😄

[React] react-beautiful-dnd로 드래그 앤 드롭 활성화하기

구현하고자 하는 기능

 

구현 방법

라이브러리 설치하기

npm install react-beautiful-dnd --save

우선 라이브러리를 사용하려면 다음과 같은 명령어를 입력해서 설치부터 해준다.

여담으로, 뒤에 --save를 하는 이유는 패키지를 설치할 때 해당 패키지를 프로젝트의 'package.json' 파일에 의존성으로 추가하도록 지시하기 위함이다. 의존성으로 추가된 패키지 정보는 'dependencies' 항목에 기록되며, 이렇게 정의된 패키지는 나중에 'npm install' 명령을 통해 프로젝트를 복원할 때 자동으로 설치된다.

 

기본 구조

<DragDropContext>
    <Droppable>
        <Draggble>List</Draggble>
        <Draggble>List</Draggble>
        <Draggble>List</Draggble>
    </Droppable>
</DragDropContext>

대략적인 구조는 다음과 같다. 가장 바깥에서 드래그 앤 드롭을 사용할 영역을 <DragDropContext>로 크게 감싸준다.

다음으로는 <Droppable>로 드래그 앤 드롭을 사용할 리스트들을 감싸준다. 마지막으로, 드래그 앤 드롭 기능을

넣어줄 요소들을 <Draggble>로 감싸주면 된다. 일단 기본적인 구조는 이렇다.

 

기능 구현하기

<DragDropContext onDragEnd={handleEnd}>
    <Droppable droppableId="todo">
        {(provided) => (
            <div {...provided.droppableProps} ref={provided.innerRef}>
                {todoData.map((data, index) => (
                    <Draggble
                        key={data.id}
                        draggableId={data.id.toString()}
                        index={index}
                    >
                        {(provided, snapshot) => (
                            <div key={data.id} 
                                {...provided.draggableProps} 
                                ref={provided.innerRef}
                                className={`${snapshot.isDragging ? "bg-fff" : "bg-fff"}`}
                            >
                               	...contents
                            </div>
                        )}
                    </Draggble>
                ))}
                {provided.placeholder}
            </div>
        )}
    </Droppable>
</DragDropContext>

드래그 앤 드롭 기능을 구현한 전체적인 코드는 위와 같다.

 

1) <Droppable> 영역 구현하기

우선, Droppable의 id 값을 설정해 준다. 현재 구현하고자 하는 앱은 todoList이므로, "todo"라고 해주었다.

Droppable 안에 provided라는 인자를 받아서 <Draggble> 전체를 감싸주었다.

드래그 앤 드롭 기능이 구현될 전체 요소들을 감싸주는 <div> 요소를 하나 만들고, 그 안에 provided.droppableProps

전달해 주고, ref로는 provided.innerRef를 설정해 준다.

 

2) <Draggable> 영역 구현하기

Droppable과 똑같이 id값을 설정해 준다. map()으로 반복되는 요소이므로 key값을 넣어주었고, 순서를 위한 index 값도

넣어주었다. provided와 snapshot 인자를 받아서 드래그 앤 드롭 기능을 수행할 때의 이벤트 동작에 대한 정보들을

provided.draggbleProps로 전달해 주고 마찬가지로 ref를 설정해 주었다.

snapshot.isDragging은 현재 드래그된 상태인가를 반환해 주는 값으로 위 코드의 경우에는 드래그가 된 상태와 드롭된 상태 모두 흰 배경으로 하고 싶어서 같은 클래스 이름을 설정해 주었다. (드래그됐을 때 : 드롭됐을 때)

 

여기서 주의할 점은 Draggble 요소 자체에 배경색을 주면 안 된다는 점이다. 배경색을 주면 드래그를 했을 때도 그 자리에 배경색이 그대로 남아 있어서 드래그되는 요소를 가리게 된다. 따라서, snapshot을 통해 드래그됐을 때와 드롭됐을 때의 배경색을 각각 설정해주어야 한다.

 

3) provided.placeholder

드래그 앤 드롭 기능의 부자연스러움을 없애주기 위해 필수로 추가해 준다.

 

const [todoData, setTodoData] = useState([]);

const handleEnd = (result) => {
    if (!result.destination) return;
    const newTodoData = [...todoData];

    // 변경시키는 아이템을 배열에서 삭제하고, return 값으로 지워진 아이템을 잡아준다.
    const [reorderedItem] = newTodoData.splice(result.source.index, 1);

    // 원하는 자리에 reorderedItem을 삽입해준다.
    newTodoData.splice(result.destination.index, 0, reorderedItem);
    setTodoData(newTodoData);
}

4) <DragDropContext>에 onDragEnd로 함수 전달하기

1~3까지가 겉으로 보이는 드래그 앤 드롭의 효과만을 구현한 것이라면, 이번에는 실제로 드래그 앤 드롭을 했을 때

데이터의 순서가 바뀌는 것을 구현하는 단계이다. 

handleEnd 함수는 result를 인자로 받고 제자리일 때는 return, 자리를 이동했을 때는 reorderedItem 변수에 이동한 자리의 아이템 값을 할당해 준다. 그 후 splice() 메소드를 이용해서 원하는 자리에 reorderedItem를 삽입해 준다.

즉, 드래그 한 아이템을 배열에서 삭제 => 드롭한 자리에 다시 삽입하는 것이다.

 

1. <DragDropContext> 안에 <Droppable> 안에 <Droggable>
2. <Droppable>과 <Droggable>은 각각 안에 provided Props를 전달해 주고, ref를 설정해 준다.
3. <Draggable>의 마지막 형제 요소로 {provided.placeholder}는 필수!
4. 드롭했을 때의 기능은 <DragDropContext>의 onDragEnd에 함수로 전달하기