Drag and Drop (DnD) ꡬν
πμμνλ©°
κ°λ¨ν ν μ΄ νλ‘μ νΈλ₯Ό μ§ννκ³ μλ€! μ¬μ€ μ½λλ§ κ°λ¨ν μ°μ΅μ©μΌλ‘ μ¨λ³΄λ €κ³ νλλ° μμ λλ κ°μ΄ μ‘νμΌ(?) λ μ¬λ°λ λ²μ΄λΌβ¦!π
νλ‘μ νΈλ ν¬μΌλͺ¬APIμ GraphQLμ νμ©νλ κ±΄λ° μ΄ λ°μ΄ν°λ₯Ό μ΄λ»κ² μ¬λ°κ² νμ©ν΄λ³ΌκΉ νλ€κ°, μ΄λ Έμ λ λν λλ‘ ν¬μΌλͺ¬ κ²μμ νλ κ²μ λ μ¬λ €λ΄€λ€.
νλ μ΄μ΄κ° κ°μ§κ³ μλ μ€νν ν¬μΌλͺ¬μ κ°κ° μμκ° μμ΄μ, κ°μ₯ λ¨Όμ μ리μ‘μ ν¬μΌλͺ¬μ νλ μ΄μ΄λ₯Ό λ°λΌλ€λκ±°λ, 맨 μ²μμ λ°°ν μμκ° λκ³€ ν΄μ λ°°ν μμ μ μ μ΄λ° μμλ₯Ό λλ¦λλ‘ μ‘°μ νκ³€ νλλ°, μ΄κ±Έ Drag and DropμΌλ‘ μ‘°μ νλ κ² κ°λ€. (μ΄λ Έμ λ κΈ°μ΅μ΄λΌ μ ννμ§ μλ€β¦π )
μ΄μ¨λ , μ΄λ²μ μ΄κ±Έ μ§μ ꡬνν΄λ³΄λ €κ³ DnD ꡬν λ°©λ²μ μ°Ύλ€κ°, μμ Web APIμμ μ 곡νκ³ μλ€λ κ²μ μκ² λμκ³ ! λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ§μκ³ , λλ¦λλ‘ κ΅¬νν λ΄μ©μ μ 리νκ³ μ νλ€.
β drag μ΄λ²€νΈ
β‘οΈdraggble
dragμ΄λ²€νΈλ μ¬μ©μκ° μμλ ν
μ€νΈλ₯Ό λλκ·Ένλ λμ λ§€ μλ°± λ°λ¦¬μ΄λ§λ€ λ°μνλ μ΄λ²€νΈλ€. μ΄λ° μ΄λ²€νΈλ κΈ°λ³Έμ μΈ λͺ¨λ μ΄λ²€νΈμμ μλνλ κ²μ΄ μλλΌ ν΄λΉ μμκ° dragκ° κ°λ₯νλ€λ κ²μ λ¨Όμ μλ €μ€μΌ νλ€. ν€μλλ draggableλ‘, dragλ₯Ό μνλ μμμ μμ±ν΄μ£Όλ©΄ λλ€.
1
2
<div draggable="true">λλκ·Έ κ°λ₯</div>
<div>λλκ·Έ λΆκ°λ₯</div>
β‘οΈdrag μ΄λ²€νΈ μ’ λ₯
DnDλ₯Ό μκ°ν΄λ³΄λ©΄ drag μ΄λ²€νΈμλ λ€μν μ’ λ₯κ° μμ μ λ°μ μλ€. μλ₯Ό λ€μ΄, dragλ₯Ό μμνμ λ, dropνμ λ λ±! μ¦ κ΄λ ¨λ μ΄λ²€νΈλ μ’ λ₯κ° μ¬λΏ μλ€.
| μ΄λ²€νΈ | μ΄λ²€νΈ νΈλ€λ¬ | μ€λͺ |
|---|---|---|
| drag | ondrag | μμλ ν μ€νΈ λΈλ‘μ λλκ·Έ ν λ λ°μνλ€. |
| dragend | ondragend | λλκ·Έλ₯Ό λλμ λ λ°μνλ€. (λ§μ°μ€ λ²νΌμ λΌκ±°λ ESC ν€λ₯Ό λλ₯Ό λ) |
| dragenter | ondragenter | λλκ·Έν μμλ ν μ€νΈ λΈλ‘μ μ ν©ν λλ‘ λμμμ μ¬λΌκ°μ λ λ°μνλ€. |
| dragexit | ondragexit | μμκ° λ μ΄μ λλκ·Έμ μ§μ μ μΈ λμμ΄ μλ λ λ°μνλ€. |
| dragleave | ondragleave | λλκ·Ένλ μμλ ν μ€νΈ λΈλ‘μ΄ μ ν©ν λλ‘ λμμμ λ²μ΄λ¬μ λ λ°μνλ€. |
| dragover | ondragover | μμλ ν μ€νΈ λΈλ‘μ μ ν©ν λλ‘ λμ μλ‘ μ§λκ° λ λ°μνλ€. (λ§€ μλ°± λ°λ¦¬μ΄λ§λ€ λ°μνλ€.) |
| dragstart | ondragstart | μ¬μ©μκ° μμλ ν μ€νΈ λΈλ‘μ λλκ·ΈνκΈ° μμνμ λ λ°μνλ€. |
| drop | ondrop | μμλ ν μ€νΈ λΈλ‘μ μ ν©ν λλ‘ λμμ λλ‘νμ λ λ°μνλ€. |
μ΄ μ€μμ, DnD ꡬνμ μ¬μ©ν μ΄λ²€νΈλ ondragenter, ondragstart, ondragend 3κ°μ§λ€.
β ꡬν νλ¦
λλ νμ¬ Next.jsλ₯Ό μ΄μ©νκ³ μμ΄μ μκ°νλ νλ¦μ λ€μκ³Ό κ°λ€.
- apiλ₯Ό λ°μμ¨λ€.
- μ¬μ©μκ° μ νν μ€νν ν¬μΌλͺ¬μ λ°°μ΄ ννλ‘ μ μ μνκ΄λ¦¬νλ€.
- μ¬μ©μκ° λλκ·Έ ν λ΄μ©μ λ°νμΌλ‘ μνλ₯Ό μ λ°μ΄νΈ νλ€.
- ν΄λΉ λ΄μ©μ λ λλ§ νλ€.
1λ²κ³Ό 2λ² λ΄μ©μ μλ΅νκ³ κ°λ¨ν μμ λ°μ΄ν°λ₯Ό κ°μ§κ³ μμ±ν μ½λλ λ€μκ³Ό κ°λ€.
β‘οΈstate μ μ
νμν stateλ₯Ό μ μνλ€. μμ λ°μ΄ν° arrκ³Ό dragνλ itemκ³Ό dropμ리μ μλ itemμ stateλ₯Ό μ μν΄μ£Όμλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//λ°μ΄ν° λ°°μ΄
let [arr, setArr] = useState<string[]>([
"νΌμΉ΄μΈ1",
"λΌμ΄μΈ2",
"νμ΄λ¦¬3",
"κΌ¬λΆμ΄4",
])
//dragνλ item
let [dragItem, setDragItem] = useState<HTMLDivElement | null>(null)
//dropνλ μμΉμ μλ item
let [destinationItem, setDestinationItem] = useState<HTMLDivElement | null>(
null
)
β‘οΈμ΄λ²€νΈ μ μ
μ΄λ²€νΈ μ’ λ₯λ 3κ°μ§λ₯Ό μ¬μ©νλ€.
- νμ¬ drag νκ³ μλ μμ΄ν μ μ°ΎκΈ°
- dropλλ μμΉ μμ΄ν μ°ΎκΈ°
- drop μ΄ν arr μ λ°μ΄νΈ νκΈ°
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//νμ¬ λλκ·Έ νλ μμ΄ν
μ 무μμΈκ°?
const dragStart = (e: React.DragEvent<HTMLDivElement>) => {
const targetItem = e.target as HTMLDivElement
setDragItem(targetItem)
}
//μ΄λ€ μμ΄ν
μλ‘ λλκ·Έ λλκ°?
const dragDestination = (e: React.DragEvent<HTMLDivElement>) => {
const targetItem = e.target as HTMLDivElement
setDestinationItem(targetItem)
}
//dropν λ°μ΄ν°λ₯Ό μ¬μ λΉνλ€.
const dropNow = () => {
// drop μ리μ μΈλ±μ€ μ°ΎκΈ°
const destinationIndex = arr.findIndex(
(item) => destinationItem?.innerText === item
)
// drag μ리μ μΈλ±μ€ μ°ΎκΈ°
const dragIndex = arr.findIndex((item) => dragItem?.innerText === item)
// arr μμ λ°κΎΈκΈ°
if (
dragIndex !== -1 &&
destinationIndex !== -1 &&
//dragνλ μΈλ±μ€μ destination μΈλ±μ€κ° κ°μΌλ©΄ κ΅³μ΄ μ€νν νμκ° μλ€.
dragIndex !== destinationIndex
) {
let newArr = [...arr]
//drag νλ itemμ κΊΌλ΄κ³ , newArrμμ ν΄λΉ itemμ μμ ν¨
const [draggedItem] = newArr.splice(dragIndex, 1)
//κΊΌλΈ μμ΄ν
μ μ μ ν μμΉμ λ£μ
newArr.splice(destinationIndex, 0, draggedItem)
setArr(newArr)
}
}
β‘οΈλ λλ§
μμμ μμ±ν κ²κ³Ό κ°μ΄ draggable νμμ μ μν΄ λ ν¨μλ₯Ό λ£μ΄μ£Όλ©΄ λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//μλ΅
{
arr.map((item, i) => {
return (
<div
key={i}
className={`bg-red-100 box-${i + 1} h-[300px]`}
draggable
onDragEnter={(e) => {
dragDestination(e)
}}
onDragStart={(e) => {
dragStart(e)
}}
onDragEnd={() => {
dropNow()
}}
>
{item}
</div>
)
})
}