Notice
Recent Posts
Archives
Today
Total
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Recent Comments
관리 메뉴

우당탕탕 개발일지

[React] react-beautiful-dnd : Drag and Drop 구현하기 본문

React

[React] react-beautiful-dnd : Drag and Drop 구현하기

devchop 2024. 5. 21. 17:17

 

 

추가))

react-beautiful-dnd 로 구현을 끝냈을 때 계속 아래와 같은 warning이 나타났다. 실행하는데는 문제 없지만, 난 빨간색 로그를 굉장히 싫어하므로 없애고싶다. 이유를 검색해보니 현재 react-beautiful-dnd는  react-redux의 오래된 버전을 사용하고 있다고 한다. 그런데 beautiful-dnd 에서는 더이상 라이브러리를 업데이트하지않는다고.. 하기 때문에.. 다른 라이브러리로 바꾸는것이 좋겠다..

 

사용할 것은 @hello-pangea/dnd 이다. 모든 동작은 beautiful-dnd와 동일하다 . 내용은 아래쪽에 수정해놓았다.

beautiful-dnd 경고문구


 

 

트렐로에서 사용하기만 했지 어떻게 만들지 생각조차 해보지 못했던... 드래그앤드롭을 구현해보도록 하겠다. 

react-beautiful-dnd 에서 퍼옴

 

공식문서는 여기.. 이미지들은 아래 문서에서 퍼왔다. 

https://www.npmjs.com/package/react-beautiful-dnd

 

react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React. Latest version: 13.1.1, last published: 2 years ago. Start using react-beautiful-dnd in your project by running `npm i react-beautiful-dnd`. There are 1885 other projects in the npm registry usin

www.npmjs.com

 

 

1. 프로젝트에 설치하기 : @hello-pangea/dnd 로 변경

npm install react-beautiful-dnd --save

변경후

npm install @hello-pangea/dnd

 

2. import 문을 이용해 사용준비.   @hello-pangea/dnd로 변경

기능을 이용할 스크립트 최상단에 아래 import문 입력한다.

import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";

변경 후 

import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";

 

3. 코드에 3가지 요소 넣어주기

 

DragDropContext , Draggable, Droppable 의 구조는 다음과 같다. 

DragDropContext : 드래그드롭을 이용할 컨텍스트 전체를 감싼다.

Droppable : 드래그를 해서 놓을수 있는 공간을 의미한다.

Draggable : 드래그를 할 수 있는 아이템을 의미한다.

DragDrop context의 구조

 

List.js

위는 내가 구현중인 todoList의 리스트 부분이다. 여기서 각 아이템을 드래그하여 순서를 변경할 수 있도록 기능을 만들려고 한다. 적절한 곳에 세 가지 요소를 넣는다. 

 

4. provided object 적용하기

provided object는 드래그 할 때의 스타일, 그리고 조회를 위한 속성이 들어있다. Draggable 과 Droppable에 적용시켜줘야 정상적으로 작동한다.

 

 4-1 . Droppable

 - Droppable에 droppableId 부여하기. 아이디는 마음대로정한다.

 - provided를 넘겨주고, 하단을 <div>태그로 감싸기

 - div안에 정보 넘겨주기 : droppableProps, innerRef

droppable 에 provided 오브젝트 적용한 결과

 

 4-2. Draggable

 - 각 draggable item에 key, draggableId, index 부여하기

 - provied를 넘겨주고, 하단을 <div> 태그로 감싸기 

 - div안에 정보 넘겨주기 : DraggableProps, innerRef, dragHandleProps

 

droppable , draggable 에 provided를 모두 적용한 결과

 

여기까지 수행하면 드래그가 가능하게 된다. 하지만 드롭을 했을 때 결과가 변하지 않는다.

 

5. 드래그가 끝났을 때 작업 

<DragDropContext>안에 이벤트를 넣어서 관리할 수 있다. 인자로는 draggableId, droppableId, destination등 여러 정보가 들어있다. 해야할 작업은 다음과 같다. 

 - 만약 목적지가 없을 경우 리턴해준다.

 - 기존 todo List에서 위치를 변경해준다 어떻게?? 

  1) 내가 선택한 카드를 리스트에서 제거한다. (다른곳에 저장해놓는다)

  2)내가 선택한 카드를 원하는 인덱스에 넣어준다

 

 todoList 위치를 변경할 땐 splice() 함수를 사용한다. 삭제/교체/추가할 수 있는 기능이다. 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

 

Array.prototype.splice() - JavaScript | MDN

splice() 메서드는 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경합니다.

developer.mozilla.org

 

drag가 종료되었을 때 함수 적용

DragDropContext에 onDragEnd 이벤트가 추가되었다. 

이제 DragEndHandler()함수를 보자.

인자로 받은 result 를 통해  source와 destination에 대한 정보를 사용할 수 있다.

1. todoData를 복사한 newTodoData를 생성한다.

2. splice함수를 통해 내가 선택한 아이템의 원래 index , 즉 result.source.index 로 부터 한개 삭제하고, 결과를 saveItem에 담는다.

3. 내가 놓은 자리의 index, 즉 result.destination.index 자리에 saveItem을 추가한다.

 

 

리액트 불변성 지키기

 

그런데 1. todoData를 왜 newTodoData로 복사를 해야하는가?? 그것은 리액트 불변성을 지키기 위해서이다.

데이터에는 원시타입과 참조타입이 있다.

원시타입 : 고정된 크기로, call stack 메모리에 저장되고 , 실제 데이터가 변수에 할당된다. (Javascript에서의 int, string, number, symbol ) 원시타입 데이터의 값을 변경할 때, 메모리안에 있는 값이 교체되는것이 아니라 아예 다른 공간에 새로운 데이터가 새로 할당해서 이사를 간다. 예를들어 string name = "A" 에서 "B"로 변경될 때 , 기존 "A"값은 그대로 남아있고 새로운 B 메모리로 이동하게된다. 즉, A라는 원본 데이터가 남는다. 

참조타입 : 데이터의 크기가 정해지지 않고 call stack 메모리에 저장된다. 데이터 값은 heap 에 저장되며, 변수에는 heap의 주소가 저장된다. (Javascript에서의 object 와 array )  참조타입은 값이 변경 될 때  Heap 내의 데이터가 변경된다. 따라서 heap 주소를 저장하고 있던 콜스택의 변수값은 변경되지 않는다. 따라서 변경하기 전의 원본데이터가 남지 않게된다.

 

불변성을 왜 지켜야할까??

참조타입에서 값을 변경하게 될 경우 원본데이터 자체가 변경되어 버리기 때문에, 이 변수를 참조하고 있던 다른 객체에서 예상하지 못한 오류가 발생할 수 있다. 

또, ReactVirtualDom에서 이전값과 변경된값을 비교하여 변경된 부분만 갱신하는 작업을 하고있는데, 원본데이터가 바뀌어버리면 변경된 부분을 정상적으로 찾지 못하게 된다. 때문에 참조타입의 경우 원본데이터를 남겨주어야한다.

 

어떻게 원본데이터를 남길까?? 

아예 새로운 배열을 하나 생성해서 값을 복사해 사용하면 된다. 또한 함수마다 불변성을 제공하는 함수가 있고 아닌것이 있으니 , 잘 판별해서 사용해야한다.

 

 

 


 

BONUS) drag중일 때 스타일 적용하기

보통 트렐로같은데에서는 드래그중이면 카드가 약간 비스듬하거나, 반투명상태로 변하는 기능을 볼 수 있다. 이처럼 드래그 되었을때 특정 스타일을 적용하고 싶을 경우 아래처럼 해주면 된다. 여기선 간단하게 배경색을 다르게 하도록 한다. 

위치는 Draggable 안에 provided 오브젝트를 받은 뒤, 스타일 적용을 원하는 곳에서 snapshot.isDragging 변수를 이용해 판별 후 스타일을 적용해주면 된다. 

draggable 의 <div> 안에 드래그중일때 스타일 적용하기

 

 

BONUS) placeholder 적용하기

드래그 했을 때 어색한 부분을 개선하기 위해서 placeholder를 아래와 같이 적용한다. 위치는 Droppable 하단 <div>의 내부, 맨 마지막에 추가하면 된다.

{provided.placeholder} 를 추가할 경우 드래그 시 UI가 어색하게 변경되는 것을 막아준다.