
이번 프로젝트에서 구현해야 했던 기능은 다음과 같았다. 사용자가 왼쪽 리스트에서 퀘스트를 선택하여 오른쪽 리스트에 추가하거나, 드래그 앤 드롭으로 순서를 변경하는 UI를 제공하는 것이다. 또한, 특정 상태(완료, 진행 중)에 있는 항목은 드래그로 순서를 변경할 수 없도록 해야 했다. 이를 위해 vue-draggable-next 라이브러리를 활용해 기능을 구현했다.

vue-draggable-next는 Vue 3에서 드래그 앤 드롭 기능을 제공하는 가벼운 라이브러리로, 드래그 순서 변경 및 조건부 제어를 쉽게 구현할 수 있다. 이번 프로젝트에서는 이 라이브러리를 사용해 간결하게 요구사항을 만족시켰다.
다음은 주요 구현 코드이다:
<template>
<VueDraggableNext
v-model="quests"
@end="onDragEnd"
:move="onMove"
tag="ul"
>
<li
v-for="quest in quests"
:key="quest.id"
:class="{ 'non-draggable': quest.status === 'done' || quest.status === 'processing' }"
>
<p>{{ quest.title }}</p>
<button v-if="!quest.status" @click="deleteQuest(quest.id)">삭제</button>
</li>
</VueDraggableNext>
</template>
<script setup>
import { VueDraggableNext } from "vue-draggable-next";
import { ref } from "vue";
interface MoveEventContext<T> {
element: T; // 드래그 중인 또는 대상 아이템
index: number; // 해당 아이템의 인덱스
}
interface MoveEvent<T> {
relatedContext: MoveEventContext<T>; // 드래그가 놓이는 대상 컨텍스트
draggedContext: MoveEventContext<T>; // 드래그 중인 컨텍스트
}
const quests = ref([
{ id: 1, title: "퀘스트 1", status: "todo" },
{ id: 2, title: "퀘스트 2", status: "done" },
{ id: 3, title: "퀘스트 3", status: "processing" },
]);
const onMove = ({ relatedContext, draggedContext }: MoveEvent<QuestOrder>): boolean => {
const targetItem = relatedContext.element;
const draggedItem = draggedContext.element;
// 완료 또는 진행 중 상태의 항목은 이동 불가
if (targetItem.status === "done" || targetItem.status === "processing") {
return false;
}
return true;
};
const onDragEnd = ({ moved }: { moved?: { newIndex: number; oldIndex: number } }) => {
if (moved) {
console.log("퀘스트 순서 변경됨:", quests.value);
}
};
const deleteQuest = (questId: number) => {
quests.value = quests.value.filter((quest) => quest.id !== questId);
};
</script>
VueDraggableNext는 배열 상태를 바인딩하여 변경 사항을 자동으로 반영한다.
위 예제에서는 quests를 v-model로 연결해 드래그 후 변경된 순서를 바로 업데이트했다.
onMove 이벤트를 활용해 특정 상태(예: 완료, 진행 중)에 있는 항목은 드래그되지 않도록 제한했다.
드래그 완료 시 호출되는 onDragEnd에서 새로운 순서를 서버로 전송하거나 로컬 상태를 업데이트하도록 처리했다.
vue-draggable-next는 Vue 3 환경에서 드래그 앤 드롭을 구현하기에 매우 직관적인 라이브러리였다. 특히, onMove와 onDragEnd 같은 이벤트 핸들러를 통해 드래그 동작을 세밀하게 제어할 수 있었고, 상태 관리에 pinia를 결합해 간결하고 효율적인 코드를 작성할 수 있었다.