TIL 5/13

Rami·2025년 5월 13일

TodayILearn

목록 보기
43/61

7강

ul, li

react-beautiful-dnd로 리스트 아이템(li)을 움직이려면 다음 두 가지를 모두 적용해야 합니다.

  1. Droppable(드랍 영역) 역할을 하는 컨테이너(ul)에

    • provided.droppableProps
    • provided.innerRef
    • 그리고 렌더링 끝에 {provided.placeholder}
      를 넣어야 합니다.
  2. Draggable(드래그 대상) 역할을 하는 요소(li)에

    • provided.draggableProps
    • provided.dragHandleProps
    • provided.innerRef
      를 넣어야 드래그가 가능해집니다.

1. DroppableProps 설명

  • provided.droppableProps

    • 드랍 가능한 영역에 필요한 ARIA 속성·이벤트 핸들러들을 포함합니다.
  • provided.innerRef

    • 실제 DOM 요소를 라이브러리에 연결하기 위한 ref입니다.
  • provided.placeholder

    • 드래그 중 공간 유지를 위해 자동으로 삽입되는 빈 엘리먼트입니다.
import { Droppable } from "react-beautiful-dnd";

<Droppable droppableId="my-list">
  {(provided) => (
    <ul
      /* 1) 드랍 영역 ref 연결 */
      ref={provided.innerRef}
      /* 2) 드랍 속성들 스프레드 */
      {...provided.droppableProps}
    >
      {items.map((item, index) => (
        /* …Draggable… */
      ))}
      {/* 3) 드래그 시 리스트 레이아웃 유지를 위한 placeholder */}
      {provided.placeholder}
    </ul>
  )}
</Droppable>

2. DraggableProps & DragHandleProps 설명

  • provided.draggableProps

    • 드래그 가능한 요소에 필요한 ARIA 속성·이벤트 핸들러·스타일(transition, transform 등)을 제공합니다.
  • provided.dragHandleProps

    • 실제로 드래그를 “잡을” 수 있는 핸들 역할을 하는 속성입니다.
    • 만약 이걸 빼면 요소 전체는 움직이지 않거나, 특정 부분만 드래그 가능하게 할 수 있습니다.
  • provided.innerRef

    • DOM 요소를 Draggable 로직에 연결하기 위한 ref입니다.
import { Draggable } from "react-beautiful-dnd";

<Draggable key={item.id} draggableId={item.id} index={index}>
  {(provided) => (
    <li
      /* 1) 드래그 대상 ref 연결 */
      ref={provided.innerRef}
      /* 2) 드래그 동작에 필요한 속성들 스프레드 */
      {...provided.draggableProps}
      /* 3) 손잡이(Drag Handle) 역할 속성 스프레드 */
      {...provided.dragHandleProps}
    >
      {item.content}
    </li>
  )}
</Draggable>

실전 사용 예시

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

function App() {
  const [items, setItems] = React.useState([
    { id: "1", content: "첫 번째 아이템" },
    { id: "2", content: "두 번째 아이템" },
    // …
  ]);

  const onDragEnd = (result: DropResult) => {
    if (!result.destination) return;
    const updated = Array.from(items);
    const [moved] = updated.splice(result.source.index, 1);
    updated.splice(result.destination.index, 0, moved);
    setItems(updated);
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="droppable-ul">
        {(provided) => (
          <ul
            ref={provided.innerRef}
            {...provided.droppableProps}
            style={{ listStyle: "none", padding: 0 }}
          >
            {items.map((item, index) => (
              <Draggable key={item.id} draggableId={item.id} index={index}>
                {(provided) => (
                  <li
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={{
                      userSelect: "none",
                      padding: "8px",
                      margin: "0 0 8px 0",
                      background: "#eee",
                      ...provided.draggableProps.style,
                    }}
                  >
                    {item.content}
                  </li>
                )}
              </Draggable>
            ))}
            {provided.placeholder}
          </ul>
        )}
      </Droppable>
    </DragDropContext>
  );
}

export default App;


Droppable에서 빠뜨리면 생기는 문제

  1. provided.innerRef

    • 없으면 라이브러리가 DOM을 참조하지 못해 드랍 영역을 인식할 수 없습니다.
  2. ...provided.droppableProps

    • 없으면 브라우저가 드랍 가능한 영역으로 인식하지 못해, 드래그된 아이템이 영역 위에서 놓여도 반응하지 않습니다.
  3. {provided.placeholder}

    • 없으면 드래그 중 빈 공간이 생기지 않아, 리스트 아이템을 잡아 뗄 때 전체 레이아웃이 점프하거나 겹쳐집니다.
<Droppable droppableId="list">
  {(provided) => (
    <ul
      ref={provided.innerRef}            // 필수: ref 연결
      {...provided.droppableProps}       // 필수: 드랍 속성
    >
      {items.map(/* …Draggable… */)}
      {provided.placeholder}             // 필수: 레이아웃 유지용
    </ul>
  )}
</Droppable>

Draggable에서 빠뜨리면 생기는 문제

  1. provided.innerRef

    • 없으면 해당 아이템을 드래그·드롭 로직과 연결할 수 없어 움직이지 않습니다.
  2. ...provided.draggableProps

    • 없으면 React-Beautiful-DnD가 아이템의 위치 변화(translate, transition 등)를 적용하지 못합니다.
  3. ...provided.dragHandleProps

    • 없으면 사용자가 잡아서 드래그할 수 있는 핸들이 없습니다. (전체를 핸들로 쓰려면 draggableProps 만으로도 되지만, 권장되는 방식은 드래그 핸들을 분리하는 것)
<Draggable key={item.id} draggableId={item.id} index={i}>
  {(provided) => (
    <li
      ref={provided.innerRef}            // 필수: ref 연결
      {...provided.draggableProps}       // 필수: 드래그 속성
      {...provided.dragHandleProps}      // 필수: 잡을 수 있는 영역 지정
    >
      {item.content}
    </li>
  )}
</Draggable>

결론

  • 모두 있어야 정상 동작

    • Droppable의 innerRef, droppableProps, placeholder
    • Draggable의 innerRef, draggableProps, dragHandleProps
  • 하나라도 빠지면 드래그·드롭 중 인식, 레이아웃 유지, 제어 핸들링 등에서 오류가 생깁니다.



draggablePropsdragHandleProps 역할

1. provided.draggableProps

  • 무엇을 하나요?

    • 드래그 가능한 요소가 움직일 때 필요한 스타일(transform, transition)과 ARIA 속성들을 포함합니다.
    • 이 속성을 적용한 요소는 “드래그 대상”으로 인식되어, 움직일 때 위치가 업데이트됩니다.
  • 사용 예시

    <li
      ref={provided.innerRef}
      {...provided.draggableProps}       // 이 부분이 없으면, 드래그는 시도되지만 움직이지 않거나 레이아웃이 꼬집니다.
    ></li>

2. provided.dragHandleProps

  • 무엇을 하나요?

    • 사용자가 드래그를 시작할 수 있는 영역(“손잡이”)에 마우스/터치 이벤트 핸들러를 바인딩합니다.
    • 이 속성을 붙인 부분을 눌러야 드래그가 실제로 시작됩니다.
  • 이 속성이 없으면?

    • draggableProps로 스타일은 적용되지만, “어디서 잡아야 드래그가 시작되는지”에 대한 이벤트가 연결되어 있지 않아 드래그가 동작하지 않습니다.
  • 사용 예시

    <li
      ref={provided.innerRef}
      {...provided.draggableProps}      // 위치 제어용
    >
      {/* 이 span을 잡아야만 드래그가 시작된다 */}
      <span {...provided.dragHandleProps}></span>
      아이템 내용
    </li>

3. 전체(전체 요소를 핸들로 쓰고 싶을 때)

  • draggablePropsdragHandleProps같은 요소에 모두 적용하면, 그 요소 전체가 드래그 핸들이 됩니다.

    <li
      ref={provided.innerRef}
      {...provided.draggableProps}      // 움직임 스타일
      {...provided.dragHandleProps}     // 클릭 영역 전체를 핸들로
    >
      전체를 잡고 드래그 가능
    </li>

정리

  1. draggableProps

    • 드래그 대상의 스타일속성을 담당
  2. dragHandleProps

    • 드래그를 시작할 수 있는 영역(핸들)에 이벤트를 붙임

→ 둘 중 하나라도 빠지면

  • draggableProps 없이: 움직임 자체가 적용되지 않음
  • dragHandleProps 없이: 잡아도 드래그 시작 이벤트가 연결되지 않아 동작하지 않음

실제 코드에선 보통 이렇게 씁니다:

<Draggable draggableId={id} index={index}>
  {(provided) => (
    <li
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
    >
      {item.content}
    </li>
  )}
</Draggable>

이처럼 두 가지를 구분해서 쓰는 이유는,

  • 핸들을 별도 영역으로 지정하고 싶을 때
  • 전체 요소를 핸들로 쓰고 싶을 때
    유연하게 선택하기 위함입니다.


profile
YOLO

0개의 댓글