react-beautiful-dnd로 구현하는 드래그 앤 드롭
react-beautiful-dnd를 이용한 리액트 드래그 앤 드롭의 설치, 기본 동작 예제, onDragEnd 처리와 성능 고려사항 및 dnd 라이브러리 비교를 정리한 기술문서
목차
소개
리액트에서 목록을 옮기거나 카드 형태 UI를 구성할 때 드래그 앤 드롭 기능은 매우 자주 사용된다. react-beautiful-dnd는 사용성이 좋고 접근성도 고려된 라이브러리다. 이 글에서는 설치부터 간단한 예제 코드, 핵심 개념, 성능 주의점, dnd 라이브러리 비교까지 단계적으로 설명한다.
설치 및 기본 구조
설치는 npm 또는 yarn으로 간단하다. 핵심 컴포넌트는 DragDropContext, Droppable, Draggable이다. DragDropContext는 드래그 이벤트를 관리하고 onDragEnd로 결과를 받는다. Droppable은 드롭 가능한 영역을, Draggable은 사용자가 옮길 수 있는 항목을 의미한다.
설치 명령
npm install react-beautiful-dnd
기본 구조 개요
기본적인 파일 흐름은 다음과 같다.
- 상태에 아이템 배열을 둔다.
- DragDropContext로 전체를 감싼다.
- Droppable 내부에 Draggable들을 렌더링한다.
간단한 예제 코드
아래 예제는 목록의 순서를 바꾸는 가장 일반적인 패턴이다. JSX 내부의 <와 >는 HTML 엔티티로 표시했다.
const initialItems = [
{ id: 'item-1', content: '첫 번째' },
{ id: 'item-2', content: '두 번째' },
{ id: 'item-3', content: '세 번째' },
];
function reorder(list, startIndex, endIndex) {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
}
function App() {
const [items, setItems] = React.useState(initialItems);
function onDragEnd(result) {
const { destination, source } = result;
if (!destination) return;
if (destination.index === source.index) return;
const newItems = reorder(items, source.index, destination.index);
setItems(newItems);
}
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
코드 설명
- reorder 함수는 배열에서 항목을 제거한 뒤 목적지 인덱스에 삽입한다.
- onDragEnd의 destination이 null이면 드롭이 취소된 경우다.
- provided.placeholder는 드래그 중 레이아웃 유지에 필요하다.
핵심 개념과 주의점
react-beautiful-dnd는 내부적으로 포지셔닝을 처리한다. 따라서 CSS로 레이아웃을 잡을 때 충돌이 생기지 않도록 유의한다. 예를 들어 flexbox나 grid 사용 시 각 항목의 높이와 마진 처리를 안정적으로 해야 한다. 또한 상태 업데이트는 불변성을 지켜서 처리해야 한다. 직접 배열을 수정하면 렌더링 이슈가 발생할 수 있다.
접근성
이 라이브러리는 키보드 접근성을 기본 지원한다. 키보드로 항목을 이동시키는 동작을 제공하므로 시나리오에 맞춰 추가적인 ARIA 속성을 적용할 필요가 적다. 다만 복잡한 커스텀 드래그 UI를 만들면 별도 접근성 점검이 필요하다.
성능 고려사항
큰 리스트에서는 가상화와 함께 사용해야 한다. react-window 등과 결합할 때는 약간의 트릭이 필요하다. Droppable과 Draggable이 자주 재생성되면 성능 저하가 발생하므로 key 관리와 메모이제이션을 신경쓴다. 렌더링 비용을 줄이려면 item 컴포넌트를 분리하고 React.memo를 적용하는 방식이 유효하다.
dnd 라이브러리 비교
세 가지 대표 라이브러리 비교로 선택 기준을 정리한다. 아래 키워드를 자연스럽게 고려한다: react-beautiful-dnd 예제, 리액트 드래그 앤 드롭, dnd 라이브러리 비교.
- react-beautiful-dnd: 접근성과 사용성에서 우수하다. 리스트와 칸반 보드에 적합하다.
- dnd-kit: 유연성과 작은 번들 사이즈가 장점이다. 커스텀 동작 구현이 쉬운 편이다.
- react-dnd: 낮은 수준의 API로 복잡한 드래그 플로우에 강하다. 러닝커브가 상대적으로 높다.
선택 시 고려할 점은 프로젝트 복잡도, 접근성 요구사항, 번들 크기, 커스텀 필요성이다. react-beautiful-dnd는 빠르게 구현하고 접근성 보장을 원할 때 적합하다. 반면 dnd-kit은 세부 동작을 직접 제어하고자 할 때 유리하다.
자주 겪는 문제와 해결법
- placeholder가 보이지 않을 때: provided.placeholder를 루트 레벨에 두지 않았을 가능성.
- 스타일 깨짐: CSS 우선순위나 position 속성 충돌 확인.
- 키보드 이동 미동작: 최신 버전 사용과 ARIA 속성 확인.
마무리
react-beautiful-dnd는 리액트에서 드래그 앤 드롭을 구현할 때 가장 직관적인 선택지 중 하나다. 기본 예제를 통해 onDragEnd 패턴을 익히면 다양한 UI에 응용할 수 있다. 필요에 따라 dnd-kit이나 react-dnd와 비교 검토하면 적절한 도구 선택에 도움이 된다.