μžλ°”μŠ€ν¬λ¦½νŠΈUI

바닐라 μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ λ“œλž˜κ·Έ μ•€ λ“œλ‘­ UI μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ν•˜κΈ° – μ‹€μ „ 사둀 쀑심

πŸ―κΏ€μƒμ΄πŸ 2025. 7. 11. 14:39

λ“œλž˜κ·Έ μ•€ λ“œλ‘­ UI μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„ν•˜κΈ°

 

 

μ›Ή UIμ—μ„œ μ‚¬μš©μžμ™€μ˜ μƒν˜Έμž‘μš©μ„ μžμ—°μŠ€λŸ½κ²Œ λ§Œλ“œλŠ” 방법 쀑 ν•˜λ‚˜κ°€

λ°”λ‘œ λ“œλž˜κ·Έ μ•€ λ“œλ‘­ μΈν„°νŽ˜μ΄μŠ€(Drag and Drop UI) μž…λ‹ˆλ‹€.

 

파일 첨뢀, 리슀트 μ •λ ¬, λŒ€μ‹œλ³΄λ“œ ꡬ성 λ“± λ‹€μ–‘ν•œ 싀무 μ‚¬λ‘€μ—μ„œ μ‚¬μš©λ˜λŠ” 이 κΈ°λŠ₯은

μ‚¬μš©μž κ²½ν—˜(UX)을 크게 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

이 κΈ€μ—μ„œλŠ” 바닐라 μžλ°”μŠ€ν¬λ¦½νŠΈ(Vanilla JavaScript)λ₯Ό ν™œμš©ν•˜μ—¬

λ‹€μŒ μ„Έ κ°€μ§€ μ‹€μ „ 사둀λ₯Ό μ€‘μ‹¬μœΌλ‘œ λ“œλž˜κ·Έ μ•€ λ“œλ‘­ UIλ₯Ό κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

  • βœ… Gmail μŠ€νƒ€μΌμ˜ 파일 첨뢀 λ“œλ‘­μ‘΄
  • βœ… TODO μ•±μ—μ„œ ν•  일 μˆœμ„œ λ³€κ²½ κΈ°λŠ₯
  • βœ… λŒ€μ‹œλ³΄λ“œ μœ„μ ― μœ„μΉ˜ 이동 UI

 

πŸ“‚ 1. 파일 첨뢀 λ“œλ‘­μ‘΄ UI – Gmail μŠ€νƒ€μΌ κ΅¬ν˜„

 

λ“œλž˜κ·Έ μ•€ λ“œλ‘­ λ°©μ‹μœΌλ‘œ νŒŒμΌμ„ 첨뢀할 수 μžˆλŠ” UIλŠ”

이메일, κ²Œμ‹œνŒ, κ΄€λ¦¬μž νŽ˜μ΄μ§€ λ“±μ—μ„œ 많이 μ‚¬μš©λ©λ‹ˆλ‹€.

 

πŸ”§ μ£Όμš” κΈ°λŠ₯

  • dragenter, dragover, drop 이벀트λ₯Ό ν™œμš©ν•œ λ“œλ‘­μ‘΄ κ΅¬ν˜„
  • μ—…λ‘œλ“œλœ 파일 λͺ©λ‘ μ‹€μ‹œκ°„ μ‹œκ°ν™”
  • 파일 ν˜•μ‹ 필터링 및 이미지 미리보기

 

πŸ’‘ UX κ°•ν™” 팁

  • μ‚¬μš©μžκ°€ νŒŒμΌμ„ λ“œλ‘­ν•  λ•Œ 배경색 λ³€κ²½ λ“± μ‹œκ°μ  ν”Όλ“œλ°± 제곡
  • DataTransfer.itemsλ₯Ό ν™œμš©ν•΄ 폴더 λ“œλž˜κ·Έ λ°©μ§€ 처리
 
dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const files = [...e.dataTransfer.files];
  renderFileList(files);
});

 

πŸ”ƒ 2. 리슀트 μ •λ ¬ κΈ°λŠ₯ – TODO 리슀트 ν•­λͺ© μˆœμ„œ λ³€κ²½

 

λ“œλž˜κ·Έλ₯Ό 톡해 ν•  일 λͺ©λ‘μ˜ μˆœμ„œλ₯Ό λ³€κ²½ν•  수 μžˆλ‹€λ©΄

μ‚¬μš©μžλŠ” λ”μš± μ§κ΄€μ μœΌλ‘œ 리슀트λ₯Ό μ‘°μž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

πŸ”§ μ£Όμš” κΈ°λŠ₯

  • 각 ν•­λͺ©μ— draggable="true" 속성 λΆ€μ—¬
  • dragstart, dragover, drop 이벀트둜 DOM μš”μ†Œ 좔적 및 μ •λ ¬
  • insertBefore λ©”μ„œλ“œλ₯Ό ν™œμš©ν•΄ 리슀트 μˆœμ„œ λ³€κ²½

 

πŸ’‘ UX κ°•ν™” 팁

  • λ“œλž˜κ·Έ 쀑인 ν•­λͺ©μ— dragging 클래슀 μΆ”κ°€ν•˜μ—¬ μŠ€νƒ€μΌ κ°•ν™”
  • λ“œλ‘­ μœ„μΉ˜μ— μ‹œκ°μ  힌트(placeholder DOM) μΆ”κ°€
 
list.addEventListener('drop', (e) => {
  const dragged = document.querySelector('.dragging');
  const target = e.target.closest('li');
  list.insertBefore(dragged, target);
});

 

πŸ“¦ 3. λŒ€μ‹œλ³΄λ“œ μœ„μ ― 이동 – λ°•μŠ€ 기반 λ“œλž˜κ·Έ κ΅¬ν˜„

 

HTML Drag API 외에도 mousedown, mousemove, mouseupλ₯Ό ν™œμš©ν•œ

μ»€μŠ€ν…€ λ°©μ‹μ˜ λ“œλž˜κ·Έ UI도 κ°€λŠ₯ν•©λ‹ˆλ‹€.

특히 λŒ€μ‹œλ³΄λ“œμ˜ μΉ΄λ“œλ‚˜ μœ„μ ― 배치λ₯Ό 자유둭게 μ‘°μ •ν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€.

 

πŸ”§ μ£Όμš” κΈ°λŠ₯

  • μ»€μŠ€ν…€ 이벀트 체인으둜 μœ„μΉ˜ μ œμ–΄
  • position: absolute와 left/top 속성 변경을 ν†΅ν•œ 이동
  • μ»€μ„œ μŠ€νƒ€μΌ λ³€κ²½ 및 경계선 힌트 μΆ”κ°€ κ°€λŠ₯
 
document.addEventListener('mousemove', (e) => {
  if (isDragging) {
    box.style.left = `${e.pageX - offsetX}px`;
    box.style.top = `${e.pageY - offsetY}px`;
  }
});

 

πŸ“Œ λ°˜μ‘ν˜• & μ ‘κ·Όμ„± κ³ λ €

  • λͺ¨λ°”일 λŒ€μ‘μ„ μœ„ν•΄ touchstart, touchmove μ΄λ²€νŠΈλ„ 병행 처리
  • ν‚€λ³΄λ“œ 탐색 κ°€λŠ₯ν•œ ꡬ쑰와 μ‹œκ°μ  ν”Όλ“œλ°±μ„ 톡해 μ ‘κ·Όμ„± κ°•ν™”
  • μƒνƒœ μ €μž₯을 μœ„ν•΄ localStorage와 μ—°λ™ν•˜μ—¬ UX 지속성 확보

 

🏁 결둠

 

λ“œλž˜κ·Έ μ•€ λ“œλ‘­ UIλŠ” μ‚¬μš©μž μΈν„°λž™μ…˜μ„ κ°•ν™”ν•˜λ©΄μ„œλ„

UXλ₯Ό λ§€λ„λŸ½κ²Œ λ§Œλ“€ 수 μžˆλŠ” μ€‘μš”ν•œ κΈ°λŠ₯μž…λ‹ˆλ‹€.

λΌμ΄λΈŒλŸ¬λ¦¬μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³  순수 μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ κ΅¬ν˜„ν•œλ‹€λ©΄

μž¬μ‚¬μš©μ„±κ³Ό ν™•μž₯μ„±κΉŒμ§€ 확보할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

이 κΈ€μ˜ 사둀듀을 ν™œμš©ν•΄

μ—¬λŸ¬λΆ„μ˜ ν”„λ‘œμ νŠΈμ—λ„

μžμ—°μŠ€λŸ¬μš΄ λ“œλž˜κ·Έ μ•€ λ“œλ‘­ UIλ₯Ό μ μš©ν•΄ λ³΄μ„Έμš”.