Coworkers - 팀 페이지 구현 (Drag & Drop 구현)

오병훈·2025년 3월 3일
0
post-thumbnail

3. To-Do List Drag & Drop 구현


3-1. dnd-kit 라이브러리 활용

1️⃣ dnd-kit을 선택한 이유: 다른 솔루션과의 비교

드래그 앤 드롭(Drag & Drop) 기능을 구현하기 위해 다양한 라이브러리와 방법을 비교한 결과, dnd-kit이 가장 적합하다고 판단하여 선택하게 되었습니다.

✅ dnd-kit의 주요 장점

1. 모던한 React Hook 기반 API
  • dnd-kit은 최신 React 패턴(예: Hooks)을 적극적으로 활용하여 선언적이고 직관적인 방식으로 드래그 앤 드롭을 구현할 수 있습니다.
  • 장점: 코드가 간결해지고, React의 상태 관리(useState, useEffect 등)와 자연스럽게 통합됩니다.
2. 높은 유연성과 커스터마이징 지원
  • draggable 요소와 droppable 영역을 자유롭게 구성할 수 있으며, 애니메이션 효과, 드래그 시작/종료 시의 이벤트 핸들링까지 유연하게 커스터마이징할 수 있습니다.
  • 장점: 단순한 리스트 재정렬뿐만 아니라 드래그 & 드롭을 활용한 다양한 인터랙션(UI/UX 개선)이 가능합니다.
3. 경량화 및 성능 최적화
  • dnd-kit은 불필요한 렌더링을 줄이고 최소한의 코드 크기로 동작하도록 설계되었습니다.
  • 장점: 대규모 리스트를 다루거나 빈번한 업데이트가 필요한 To-Do List 같은 애플리케이션에서도 부드러운 UX를 유지할 수 있습니다.
4. 지속적인 유지보수 및 커뮤니티 지원
  • dnd-kit은 현재 활발하게 유지보수되고 있으며, 공식 문서와 커뮤니티 자료가 많아 문제 발생 시 쉽게 해결할 수 있습니다.
  • 장점: 최신 React 기능과의 좋은 호환성을 유지하면서도, 새로운 기능들이 지속적으로 추가되고 있습니다.

2️⃣ 다른 드래그 앤 드롭 라이브러리와의 비교

라이브러리장점단점비교 결과
dnd-kit (선택)1. React Hook 기반 API 사용
2. 높은 커스터마이징 지원
3. 경량화 및 최적화
1. 일부 고급 기능(예: 리스트 내부에서 드래그 종료 후 정렬 유지)은 추가 구현 필요✅ 유연하고 모던한 기능 제공
react-beautiful-dnd1. 간단한 리스트 재정렬 구현이 용이1. 유지보수 중단 (React 최신 버전과의 호환성 문제 발생)
2. 커스터마이징 제한
❌ 최신 React와 호환되지 않아 비효율적
React DnD1. 강력한 기능 지원 (다양한 드래그 & 드롭 시나리오 구현 가능)1. API가 복잡하고 학습 곡선이 높음
2. 간단한 To-Do List 재정렬에는 과한 기능
❌ 학습 난이도가 높고 불필요한 기능이 많음
커스텀 솔루션 (IntersectionObserver 활용)1. 외부 라이브러리 의존성이 없음1. 직접 구현 시 많은 시간과 리소스 필요
2. 복잡한 기능을 완벽하게 구현하기 어려움
❌ 유지보수 부담이 크고 비효율적

📌 결론:

  • react-beautiful-dnd는 유지보수가 중단되었고, React DnD는 API가 너무 복잡하며, 직접 커스텀 구현은 시간이 오래 걸리는 단점이 있습니다.
  • dnd-kit은 최신 React와 좋은 호환성을 갖으며, 성능 최적화와 확장성을 동시에 제공하기 때문에 Coworkers 프로젝트에서 가장 적합한 선택이라고 생각했습니다.

3️⃣ dnd-kit을 이용한 구현 방식

💡 dnd-kit을 활용하여 팀 페이지 To-Do List에서 할 일 목록을 드래그 & 드롭으로 정렬하는 과정을 구현하였습니다.

📌 주요 기능

  1. 사용자가 할 일 목록을 드래그 & 드롭하여 순서를 변경할 수 있도록 구현
  2. useSensoruseSensors를 사용하여 마우스 & 터치 이벤트 모두 지원
  3. 정확한 충돌 감지를 위해 closestCenter 전략을 사용하여 최적의 UX 제공
  4. 정렬된 목록을 상태로 저장(useState)하고, 변경 즉시 서버에 업데이트
const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: { distance: 10 },
    }),
  );

 const handleDragStart = (event: DragStartEvent) => {
    const { active } = event;
    setActiveId(active.id as number);
  };
  
const handleDragEnd = (event: DragEndEvent) => {
  const { active, over } = event;
  if (!over || active.id === over.id) {
    setActiveId(null);
    return;
  }
  const oldIndex = items.findIndex((item) => item.id === active.id);
  const newIndex = items.findIndex((item) => item.id === over.id);
  if (oldIndex !== -1 && newIndex !== -1) {
    const newItems = arrayMove(items, oldIndex, newIndex);
    setItems(newItems);
    editTaskListOrder({
      groupId,
      id: Number(active.id),
      displayIndex: newIndex,
    });
    queryClient.setQueryData<GroupResponse>(
      ['group', groupId],
      (oldData?: GroupResponse): GroupResponse | undefined => {
        if (!oldData) return oldData;
        return {
        ...oldData,
        taskLists: newItems,
        };
	  },
    );
  }
  setActiveId(null);
};

return(
  <DndContext
    collisionDetection={closestCenter} // 가장 가까운 요소를 감지하여 올바른 위치로 드롭
    sensors={sensors} // 마우스 및 터치 이벤트 감지
    onDragStart={handleDragStart} // 드래그 시작 시 실행
    onDragEnd={handleDragEnd} // 드래그 종료 후 실행
  >
    {/* SortableContext: 리스트 아이템을 정렬 가능한 컨텍스트로 설정 */}
    <SortableContext items={items} strategy={verticalListSortingStrategy}>
      {/* 할 일 목록을 렌더링하는 컨테이너 */}
	  <div className="할 일 목록을 표시하는 컨테이너 (스크롤 적용 가능)">
   	  </div>
    </SortableContext>

    {/* DragOverlay: 드래그 중인 아이템을 오버레이 형태로 표시 */}
    <DragOverlay>
      {/* 드래그 중인 아이템을 별도로 렌더링하여 UI 일관성 유지 */}
    </DragOverlay>
  </DndContext>
);

📌 결론:

  • 기존에는 할 일 순서를 변경할 방법이 없어 UX가 불편했으나, dnd-kit을 활용하여 간편한 드래그 앤 드롭 인터페이스를 제공할 수 있었습니다.
  • 순서 변경 후 즉시 서버와 동기화되도록 구현하여, 새로고침 없이도 UI가 업데이트되었습니다.

3-2. dnd-kit Drag & Drop 오류 해결 과정

팀 프로젝트에서 dnd-kit을 활용하여 할 일 목록(TodoList)의 Drag & Drop 기능을 구현하던 중 모바일 환경(특히 iOS)에서 드래그가 정상적으로 동작하지 않는 문제를 발견했습니다.

이를 해결하기 위해 다양한 접근 방식을 시도했고, 최종적으로 스타일 수정과 센서(sensor) 최적화를 통해 문제를 해결하였습니다.
이 글에서는 발생한 문제, 원인 분석, 해결 과정을 정리하겠습니다.

📌 1. iOS에서 드래그 동작 불가 문제

⚠️ 발생한 문제

  • iOS 모바일 기기에서 할 일 목록을 드래그할 때 제대로 작동하지 않는 현상 발생
  • 아이템을 길게 눌렀을 때 링크가 열리거나 "복사/저장" 같은 기본 컨텍스트 메뉴가 표시됨
  • 이로 인해 드래그 동작이 차단되거나, 터치 이벤트가 정상적으로 전달되지 않음

🔍 원인 분석

iOS Safari를 포함한 일부 모바일 브라우저는 터치 기반 UI에서 기본 동작(예: 텍스트 선택, 컨텍스트 메뉴, 링크 클릭 등)을 제공하기 때문에,
이를 적절히 제어하지 않으면 드래그 이벤트보다 기본 이벤트가 우선 실행될 수 있습니다.

✅ 해결 방법

CSS 스타일을 수정하여 터치 이벤트의 기본 동작을 차단하면 정상적인 드래그 기능을 구현할 수 있습니다.

WebkitUserSelect: 'none', // 텍스트 선택 방지 (iOS 및 WebKit 기반 브라우저)
WebkitTouchCallout: 'none', // iOS Safari에서 기본 컨텍스트 메뉴(복사, 저장 등) 비활성화

iOS 기본 컨텍스트 메뉴 차단 - Webkit 설정 추가

👉 스타일 적용 후 드래그가 정상적으로 동작

이제 iOS에서도 기본 컨텍스트 메뉴가 뜨지 않고, 올바르게 Drag & Drop이 작동합니다.


📌 2. 모바일(iOS)에서 터치 감도가 비정상적으로 동작하는 문제

⚠️ 발생한 문제

  • 마우스 환경에서는 드래그가 정상 동작하지만,모바일 환경에서는 터치 이벤트가 인식되지 않거나 지나치게 민감하게 반응하는 문제가 발생
  • 드래그가 너무 빠르게 시작되거나, 화면을 스크롤하려 할 때도 드래그가 인식됨

🔍 원인 분석

기존에 사용하던 PointerSensor마우스, 터치, 펜 입력을 모두 감지하는 장점이 있지만,
모바일에서는 터치 이벤트를 세밀하게 제어하기 어렵고, 의도치 않은 드래그가 발생할 가능성이 높았습니다.

const sensors = useSensors(
  useSensor(PointerSensor, {
    activationConstraint: { distance: 10 }, // 10px 이상 이동해야 드래그 시작
  }),
);

❌ PointerSensor 단점

  • 터치 입력에 대한 세밀한 조정이 어렵고, 작은 움직임에도 드래그가 발생할 수 있음
  • 사용자가 화면을 스크롤하려는 의도와 드래그 이벤트가 충돌하는 문제 발생

✅ 해결 방법

👉 PointerSensor 대신 MouseSensor + TouchSensor를 조합하여 사용

터치 입력 개선 - MouseSensor + TouchSensor 적용

  • MouseSensor : 마우스 환경에서는 기존과 동일하게 10px 이상 이동 시 드래그 시작
  • TouchSensor : 모바일 터치 환경에서 더 정교한 제어를 위해 추가적인 설정 적용
const sensors = useSensors(
  useSensor(MouseSensor, {
    activationConstraint: {
      distance: 10, // 10px 이상 이동해야 드래그 시작
    },
  }),
  useSensor(TouchSensor, {
    activationConstraint: {
      delay: 150, // 150ms 이상 터치 후 드래그 시작 (짧은 터치는 클릭으로 처리)
      tolerance: 5, // 5px 이하의 미세한 움직임은 드래그로 인식하지 않음
    },
  }),
);

🎯 적용 후 개선된 점

✅ 의도치 않은 드래그 방지 → 터치가 길게 유지되었을 때만 드래그가 활성화됨
✅ 모바일 터치 감도 개선 → 손가락이 가볍게 움직이는 정도로는 드래그가 발생하지 않음
✅ 스크롤 방해 최소화 → 화면을 스크롤할 때는 드래그가 실행되지 않도록 설정


🔄 수정 전/후 비교

수정 전수정 후

👉 이제 모바일에서도 부드럽고 직관적인 드래그가 가능하게 되었음!


📝 결론

이번 개선을 통해 dnd-kit을 활용한 드래그 앤 드롭 기능이 iOS 모바일 환경에서도 정상 동작하도록 최적화할 수 있었습니다.

📌 주요 해결 방법 요약

1️⃣ iOS에서 기본 터치 이벤트 차단WebkitUserSelect: 'none', WebkitTouchCallout: 'none' 설정
2️⃣ 센서 최적화PointerSensor 대신 MouseSensor + TouchSensor로 세밀한 입력 감지
3️⃣ 터치 감도 조정delaytolerance를 조절하여 모바일에서 자연스러운 UX 구현

이 과정을 통해 PC와 모바일 환경 모두에서 안정적으로 드래그 앤 드롭이 가능해졌습니다.

📌 관련 PR 모음

profile
Front-End Developer

0개의 댓글