드래그 앤 드롭은(는) 무엇을 하나요?
규칙
@dnd-kit/core + @dnd-kit/sortable 사용 — 최신형, 접근성 좋고 터치 친화적인 DnD 라이브러리
- 정렬 가능한 목록:
<SortableContext>로 감싸고, 각 항목은 useSortable() 훅 사용
- 칸반 보드:
<DndContext> 안에 여러 개의 <SortableContext> 컨테이너 — 각 칸반 열당 하나
- 파일 드롭 존:
react-dropzone 또는 네이티브 onDragOver/onDrop + e.dataTransfer.files
- 터치 지원: dnd-kit이 기본적으로 터치를 처리 —
TouchSensor와 PointerSensor를 활성화 제약 조건과 함께 사용
- 접근성: dnd-kit이 키보드 DnD를 기본 제공 —
KeyboardSensor + sortableKeyboardCoordinates
- 낙관적 재정렬: 드롭 시 즉시 상태 업데이트, 백그라운드에서 서버에 저장, 오류 시 롤백
- 드래그 오버레이:
<DragOverlay>를 사용하여 드래그 중인 항목의 부동 미리보기 제공 — 시각적 피드백 향상
패턴
import { DndContext, closestCenter, DragOverlay } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
function SortableItem({ id, children }: { id: string; children: React.ReactNode }) {
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
const style = { transform: CSS.Transform.toString(transform), transition };
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
{children}
</div>
);
}
function SortableList({ items }: { items: Item[] }) {
const [activeId, setActiveId] = useState<string | null>(null);
return (
<DndContext
collisionDetection={closestCenter}
onDragStart={({ active }) => setActiveId(active.id)}
onDragEnd={handleDragEnd}
>
<SortableContext items={items.map((i) => i.id)} strategy={verticalListSortingStrategy}>
{items.map((item) => (
<SortableItem key={item.id} id={item.id}>{item.name}</SortableItem>
))}
</SortableContext>
<DragOverlay>{activeId ? <ItemPreview id={activeId} /> : null}</DragOverlay>
</DndContext>
);
}
피해야 할 것
- HTML5 드래그 API 직접 사용 — 터치/모바일/접근성 지원 불량
- 키보드 접근성 누락 — 항상
KeyboardSensor 포함할 것
- 드래그 중 상태 변형 —
onDragEnd에서만 업데이트할 것
DragOverlay 누락 — 드래그 중 원래 항목이 사라져 시각적 피드백이 없음