라이브러리

[react-dnd] 리액트에서 드래그앤드랍(DND) 기능 구현 방법

순코딩 2025. 7. 15. 23:52

설치

npm install react-dnd react-dnd-html5-backend

 

시작하기

프로바이더 정의

src / components / DndProviderLayer.tsx
"use client"; // 클라이언트 컴포넌트로 설정

import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

export default function DndProviderLayer({ children }: { children: React.ReactNode }) {

  const options = {
    enableMouseEvents: true,    // 마우스 이벤트 활성화
    enableTouchEvents: true,    // 터치 이벤트 활성화
    enableKeyboardEvents: true, // 키보드 이벤트 활성화 (접근성)
    ignoreContextMenu: true,    // 우클릭 메뉴 무시
    delayTouchStart: 0         // 터치 시작 지연시간 (밀리초)
  }
  
  return <DndProvider backend={HTML5Backend} options={options}>{children}</DndProvider>;
}

 

프로바이더 렌더링

src / app / layout.tsx
import DndProviderLayer from "@/lib/DndProviderLayer";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ko">
      <body>
            <DndProviderLayer>{children}</DndProviderLayer>
      </body>
    </html>
  );
}

 

드래그 컴포넌트

import { useDrag } from 'react-dnd'

interface DragItemProps {
  id: string
  name: string
}

export const DragItem = ({ id, name }: DragItemProps) => {
  // useDrag 훅을 사용하여 드래그 기능 설정
  const [{ isDragging }, drag] = useDrag(() => ({
    // 해당 드래그 아이템의 타입을 지정 (드롭 영역에서 이 타입을 받을 수 있는지 확인)
    type: 'ITEM',
    
    // 드래그할 때 전달할 데이터
    item: { id, name },
    
    // 드래그 상태 수집기 (collector)
    // monitor를 통해 현재 드래그 상태를 실시간으로 파악
    collect: (monitor) => ({
      isDragging: monitor.isDragging() // 현재 드래그 중인지 여부
    })
  }))

  return (
    // drag ref를 DOM 요소에 연결하여 드래그 가능하게 만듦
    <div 
      ref={drag as React.LegacyRef<HTMLDivElement>}
      style={{ opacity: isDragging ? 0.5 : 1 }} // 드래그 중일 때 시각적 피드백
    >
      {name}
    </div>
  )
}

 

드롭 컴포넌트

import { useDrop } from 'react-dnd'

interface DropZoneProps {
  onDrop: (item: any) => void
}

export const DropZone = ({ onDrop }: DropZoneProps) => {
  // useDrop 훅을 사용하여 드롭 영역 설정
  const [{ isOver }, drop] = useDrop(() => ({
    // 이 영역이 받아들일 수 있는 드래그 아이템의 타입
    accept: 'ITEM',
    
    // 아이템이 드롭되었을 때 실행될 콜백
    drop: (item) => onDrop(item),
    
    // 드롭 상태 수집기 (collector)
    // monitor를 통해 현재 드롭 영역의 상태를 실시간으로 파악
    collect: (monitor) => ({
      isOver: monitor.isOver() // 드래그 아이템이 드롭 영역 위에 있는지 여부
    })
  }))

  return (
    // drop ref를 DOM 요소에 연결하여 드롭 가능한 영역으로 만듦
    <div 
      ref={drop as React.LegacyRef<HTMLDivElement>}
      style={{ 
        background: isOver ? '#e0e0e0' : 'white', // 드래그 아이템이 위에 있을 때 시각적 피드백
        padding: '20px'
      }}
    >
      드롭 영역
    </div>
  )
}

 

============================================================================================

 

개발자 모드에서 DND 영역 인식 오류가 발생하는 경우 해결방법

아래 라이브러리를 설치 후 프로바이더를 아래와 같이 수정합니다

이 방법의 단점은 드래그 시 기본 아이템 UI를 제공하지 않아 드래그 중 보여지는 UI가 없습니다.

이에 드래그 중 UI를 별도로 정의해야합니다.

테스트 때문에 개발자모드를 사용하는 거라면 콘솔로그 대신 alert로 찍어보십셔

설치

npm i react-dnd-touch-backend

 

수정

src / components / DndProviderLayer.tsx
"use client"; // 클라이언트 컴포넌트로 설정

import { DndProvider } from "react-dnd";
import { TouchBackend } from "react-dnd-touch-backend";

export default function DndProviderLayer({ children }: { children: React.ReactNode }) {

  return (
    <DndProvider backend={TouchBackend} options={{ enableMouseEvents: true }}>
      {children}
    </DndProvider>
  );
}