react-beautiful-dnd
는 React 애플리케이션에서 드래그 앤 드롭 기능을 쉽게 구현할 수 있도록 도와주는 라이브러리이다. 이 라이브러리는 복잡한 드래그 앤 드롭 상호작용을 간단하게 처리할 수 있게 설계되었으며, 높은 유연성과 성능을 제공한다.
주요 특징
- 쉬운 사용법: API가 간단하고 직관적이며, 드래그 앤 드롭 기능을 쉽게 추가할 수 있다.
- 성능 최적화: 가상화와 메모이제이션을 통해 높은 성능을 유지하며, 많은 요소를 포함한 리스트에서도 원활한 성능을 보장한다.
- 접근성: 기본적으로 접근성(Accessibility, a11y)을 고려하여 설계되었다.
- 애니메이션: 드래그 및 드롭 동작에 대한 부드러운 애니메이션을 제공한다.
설치
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;
728x90
'Javascript > React' 카테고리의 다른 글
[React] 공개 라우트와 비공개 라우트 (0) | 2024.08.01 |
---|---|
[React] 리액트 라우터 (0) | 2024.08.01 |
[React] React DnD 라이브러리 (0) | 2024.07.31 |
[React] 리덕스 미들웨어 (0) | 2024.07.29 |
리듀서 활용하기 (0) | 2024.07.22 |