드래그 앤 드롭 — 손가락으로 화면에서 시각적 객체를 드래그하는 대화형 제스처입니다. 이 제스처는 세 단계로 구성됩니다: 잡기(길게 누르기 또는 터치), 이동(객체와 함께 손가락 움직이기), 놓기(대상 영역에 드롭). 요소 재정렬, 폴더 간 파일 이동, 인터페이스 구성(drag to reorder, drag to resize)에 사용됩니다. iOS에서는 UIDragInteraction을 통해, Android에서는 DragEvent 및 startDragAndDrop을 통해 구현됩니다.
핵심 사항
드래그 앤 드롭(DnD)은 사용자가 손가락이나 마우스로 객체를 잡아 다른 위치로 이동하고 놓는 그래픽 인터페이스 요소 조작 방법입니다. 모바일 인터페이스에서 드래그 앤 드롭은 일반적으로 길게 누르기로 시작됩니다 — 사용자가 요소를 300–500ms 동안 누르고 있으면 시스템이 인터페이스를 드래그 모드로 전환하고 손가락 움직임이 객체를 이동시킵니다.
역사적으로 드래그 앤 드롭은 데스크톱 인터페이스(Xerox Star, 1981; Macintosh, 1984)에서 복사/붙여넣기 명령의 대안으로 시작되었습니다. 모바일 플랫폼에서는 iOS 11(2017)의 UIDragInteraction과 Android 7.0(2016)의 멀티 윈도우 드래그 앤 드롭과 함께 완전한 지원이 나타났습니다. 그 이전에는 모바일 앱에서 드래깅이 제스처 인식기를 통한 사용자 정의 솔루션으로 구현되었습니다.
핵심 개념: 드래그 세션, 드래그 항목, 드래그 미리보기(객체의 시각적 표현), 드롭 대상, 드롭 제안(드롭 시 동작: 이동, 복사, 연결). 시스템은 운영 체제 수준에서 세션을 관리하여 다른 애플리케이션 간에 객체를 드래그할 수 있도록 합니다(예: 갤러리에서 메모로 사진).
드래그 앤 드롭은 세 가지 순차적 단계로 나뉩니다. 1단계 — 잡기(드래그 시작): 사용자가 제스처를 시작하고(일반적으로 길게 누르기), 시스템이 드래그 세션을 생성하고, 객체의 데이터를 캡처하여 드래그 미리보기를 표시합니다 — 손가락을 따라가는 요소의 반투명 복사본입니다. 이 단계에서 시스템은 DnD가 완료될 때까지 다른 제스처(스크롤, 텍스트 선택)를 거부합니다.
2단계 — 이동(드래그 이동): 손가락이 화면을 가로질러 움직이고, 드래그 미리보기가 터치 지점을 따릅니다. 시스템은 손가락 아래의 드롭 대상을 지속적으로 확인하고 drag-enter, drag-move, drag-exit 이벤트를 전송합니다. 드롭 대상은 시각적으로 반응할 수 있습니다: 강조 표시, 확장 또는 삽입선 표시. 이 단계는 작업 복잡성에 따라 0.5초에서 10초 이상 지속됩니다.
3단계 — 놓기(드롭): 손가락이 화면에서 떨어집니다. 시스템이 드래그 세션을 종료하고 대상 요소의 드롭 콜백을 호출합니다. 드롭 대상이 드롭을 거부하면(예: 쓰기 권한이 없는 폴더에 파일 드래그), 드래그 미리보기가 애니메이션과 함께 원래 위치로 돌아갑니다(취소 애니메이션). 드롭이 수락되면 요소가 원래 위치에서 사라지고 대상에 나타납니다.
| 단계 | iOS 이벤트 | Android 이벤트 | 시각적 상태 |
|---|---|---|---|
| 잡기 | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | 손가락 아래에 드래그 미리보기 표시 |
| 이동 | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | 미리보기 이동, 대상 강조 표시 |
| 놓기 | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | 드롭 또는 복귀 애니메이션 |
앱 간 드래깅은 두 플랫폼 모두에서 사용 가능한 기능입니다. iOS는 Split View 및 Slide Over 모드(iPad)에서 앱 간 드래그 앤 드롭을 지원합니다. Android는 Split Screen 모드에서 앱 간 드래깅을 지원합니다. 데이터는 UIPasteConfiguration(iOS) 또는 ClipData(Android)를 통해 전송됩니다. 개발자는 지원되는 데이터 유형(iOS의 경우 UTI, Android의 경우 MIME)을 지정합니다.
UIDragInteraction은 iOS 11에 추가된 드래깅용 주요 iOS API입니다. View에서 드래깅을 활성화하려면 UIDragInteractionDelegate와 함께 UIDragInteraction을 추가하세요. 델리게이트는 어떤 요소를 드래그할 수 있는지, 해당 요소의 모양과 동작을 정의합니다. 요소를 수신하려면 UIDropInteractionDelegate와 함께 UIDropInteraction을 사용하여 View가 드롭을 수락할 수 있는지와 데이터 처리 방법을 결정합니다.
// UIDragInteraction — 컬렉션에서 요소 드래그
class DragViewController: UIViewController, UIDragInteractionDelegate {
@IBOutlet var dragSourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let dragInteraction = UIDragInteraction(delegate: self)
dragSourceView.addInteraction(dragInteraction)
}
func dragInteraction(_ interaction: UIDragInteraction,
itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let data = Data(contentsOf: selectedFileURL) else { return [] }
let itemProvider = NSItemProvider(object: data as NSData)
let dragItem = UIDragItem(itemProvider: itemProvider)
// 드래그 미리보기 — 요소의 축소된 복사본
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate는 드롭 수신을 위한 미러 인터페이스입니다. canHandle(_:) 메서드는 데이터 유형이 지원되는지 확인합니다. sessionDidUpdate(_:)는 시각적으로 상태를 업데이트합니다(강조 표시, 삽입선). performDrop(_:)는 최종 데이터 처리를 수행합니다. iOS는 자동으로 작업 유형(.move, .copy, .forbidden)과 함께 UIDropProposal을 호출합니다. 작업 유형은 시각적 표시에 영향을 줍니다(복사의 경우 미리보기 모서리에 + 아이콘, forbidden의 경우 금지 표시).
View.startDragAndDrop()은 API 24(Android 7.0)부터 사용 가능한 드래그를 시작하는 Android SDK 메서드입니다. 요소를 잡은 후(일반적으로 onLongClick에서) 호출됩니다. 매개변수: ClipData(데이터), DragShadowBuilder(시각적 그림자), Object myLocalState(로컬 상태), flags. DragShadowBuilder는 손가락을 따라가는 Bitmap을 생성합니다. 시스템은 드래그 중 애니메이션과 터치 이벤트를 자동으로 관리합니다.
// 재정렬이 있는 RecyclerView 요소의 드래그 앤 드롭
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// 데이터 전송을 위한 ClipData 생성
val clipData = ClipData.newPlainText("item", items[position])
// 축소된 미리보기가 있는 사용자 정의 DragShadowBuilder
val shadowBuilder = object : View.DragShadowBuilder(holder.itemView) {
override fun onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point) {
shadowSize.set(itemView.width, itemView.height / 2)
}
}
holder.itemView.startDragAndDrop(clipData, shadowBuilder, position, 0)
true
}
}
}드롭 수신은 Android에서 View.setOnDragListener()를 통해 구현됩니다. 리스너는 ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, ACTION_DROP, ACTION_DRAG_ENDED 이벤트를 수신합니다. ACTION_DROP 이벤트에서 ClipData에서 데이터를 추출하고 대상 작업을 수행합니다. 재정렬이 있는 RecyclerView의 경우 ItemTouchHelper.SimpleCallback이 권장됩니다 — 잡기를 위한 길게 누르기, 이동 애니메이션 및 onMove/onSwiped 유틸리티 메서드를 자동으로 처리하는 내장 메커니즘입니다.
목록 재정렬(drag to reorder)은 가장 일반적인 DnD 시나리오입니다: 사용자가 목록 항목을 드래그하여 순서를 변경합니다. 예: 즐겨찾기에서 연락처 재정렬, 트래커(Trello, Todoist)에서 작업 정렬, 재생 목록에서 트랙 재정렬(Spotify, Apple Music). Android에서는 ItemTouchHelper, iOS에서는 UILongPressGestureRecognizer + 셀 재정렬을 통해 구현됩니다.
파일 관리자 — 폴더 간 파일 복사 및 이동을 위한 드래그 앤 드롭. iOS Files App, Android Files by Google, Dropbox — 모두 DnD를 지원합니다. 파일을 폴더로 드래그하면 내부로 이동하고(move), Option/Ctrl을 누른 상태에서 드래그하면 복사됩니다(copy). 드롭 대상은 호버 시 시각적으로 강조 표시되어 폴더 경계를 보여줍니다.
칸반 보드 — Trello, Jira, Notion은 카드를 열 간에 이동하기 위해 드래그 앤 드롭을 사용합니다(To Do → In Progress → Done). 드래그하면 카드가 원래 열에서 분리되어 손가락을 따르고, 다른 열의 경계를 넘으면 애니메이션과 함께 삽입됩니다. iOS 11+는 미리보기가 터치 지점에서 약간 기울어져 깊이를 만드는 시차 효과가 있는 UIDragPreview를 지원합니다.
Springboard 섹션(iOS) — iOS 홈 화면에서 앱 아이콘 드래그. 아이콘을 길게 누르면 “떨림” 모드가 활성화되고(아이콘이 흔들림), 사용자는 페이지 간에 아이콘을 이동하고 폴더를 만들 수 있습니다. 드래그, 흔들림 애니메이션 및 폴더 그룹화를 동시에 처리해야 하므로 가장 복잡한 DnD UX 패턴 중 하나입니다.
시각적 피드백은 드래그 앤 드롭에 매우 중요합니다. 드래그되는 객체는 변경되어야 합니다: 축소(iOS: 스케일 0.8–0.9), 반투명화(알파 0.7–0.8), 그림자와 함께 인터페이스 위로 올리기(shadow offset). 드롭 대상은 호버 시 시각적으로 반응해야 합니다: 강조 표시, 확장 또는 테두리 표시. 이러한 신호가 없으면 사용자가 객체를 어디에 드롭할 수 있는지 알 수 없습니다.
복귀 애니메이션(드래그 취소) — 사용자가 드롭 영역 밖이나 유효하지 않은 영역에서 요소를 놓으면 객체가 애니메이션과 함께 원래 위치로 돌아가야 합니다. iOS UIDragInteractionDelegate는 추적을 위한 dragInteraction(_:sessionDidMove:)과 복귀 애니메이션을 위한 dragInteraction(_:session:willEndWith:)을 제공합니다. Android View.setOnDragListener는 ACTION_DRAG_ENDED를 수신하며, ValueAnimator 또는 ObjectAnimator를 통해 복귀 애니메이션을 시작할 수 있습니다.
| 권장 사항 | iOS | Android |
|---|---|---|
| 제스처 잡기 | 길게 누르기(기본값) | OnLongClickListener |
| 드래그 미리보기 | UIDragItem.previewProvider | DragShadowBuilder |
| 햅틱 피드백 | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| 복귀 애니메이션 | UIDragPreviewTarget | ViewPropertyAnimator |
| 드롭 제안 | UIDropProposal (.move/.copy) | ACTION_DROP + 플래그 |
| 앱 간 DnD | iOS 11+ Split View(iPad) | Android 7.0+ Split Screen |
접근성: 운동 장애가 있는 사용자는 드래그 제스처를 수행하지 못할 수 있습니다. 목록의 경우 “위/아래로 이동” 버튼, 파일의 경우 “폴더로 이동” 상황에 맞는 메뉴를 통해 대안을 제공하세요. iOS VoiceOver는 요소 이동을 위한 로터 동작 “드래그”를 지원합니다. Android TalkBack은 AccessibilityNodeInfo.addAction()을 통해 사용자 정의 동작을 제공합니다. Nike Run Club과 Apple Health는 대시보드에서 메트릭을 재정렬하기 위해 DnD를 사용합니다 — 표준 버튼을 통한 접근과 함께.
자주 묻는 질문
드래그 앤 드롭을 시작하는 표준 제스처는 300–500ms의 길게 누르기입니다. iOS는 내장 UIDragInteraction 메커니즘을 사용하여 지속적인 터치를 자동으로 잡기로 변환합니다. Android는 OnLongClickListener 콜백에서 startDragAndDrop()을 명시적으로 호출해야 합니다. 일부 경우(예: 게임)에는 DnD가 지연 없이 일반 터치로 시작될 수 있습니다.
네, 앱 간 드래그 앤 드롭은 iOS 11+(iPad) 및 Android 7.0+에서 지원됩니다. iPad에서는 Split View 및 Slide Over 모드에서 드래깅이 작동합니다 — 사용자가 Safari에서 텍스트를 가져와 메모에 드롭할 수 있습니다. Android에서는 Split Screen에서 앱 간 DnD가 작동합니다. 데이터는 UIPasteConfiguration(iOS) 또는 ClipData(Android)를 통해 지정된 지원 유형(UTI, MIME)과 함께 전송됩니다.
DnD와 스크롤 충돌은 잡기 지연을 통해 해결됩니다. 사용자가 길게 누르기 제한 시간(500ms)이 만료되기 전에 손가락을 움직이기 시작하면 제스처가 드래그 앤 드롭이 아닌 스크롤로 해석됩니다. iOS에서 UIDragInteraction은 cancelsTouchesInView를 통해 이 충돌을 자동으로 관리합니다. Android에서 ItemTouchHelper는 임계값을 사용합니다 — 움직임이 TouchSlop 임계값을 초과하면 스크롤이 우선되어 길게 누르기가 취소됩니다.
드래그 앤 드롭은 모든 데이터 유형을 지원합니다: 텍스트, 이미지, 파일, URL, 사용자 정의 객체. iOS는 UTI 유형(public.text, public.image, public.file-url)의 NSItemProvider를 사용합니다. Android는 MIME 유형(text/plain, image/png, application/octet-stream)의 ClipData를 사용합니다. 사용자 정의 데이터의 경우 Intent와 함께 ClipData.Item이 생성됩니다. 앱 간 DnD의 경우 데이터가 보편적 형식(일반 텍스트, HTML, RTF, UIImage/PNG)으로 패키징됩니다.
iOS에서 복귀 애니메이션은 UIDragPreviewTarget을 통해 처리됩니다 — 미리보기가 애니메이션과 함께 돌아갈 최종 위치입니다. Android에서는 translateX/translateY와 함께 ViewPropertyAnimator를 사용하여 요소를 원래 위치로 되돌립니다. 애니메이션 시간은 200–300ms이며 이징 커브(EaseOut)를 사용합니다. UIKit은 제스처가 취소될 때 부드럽게 돌아가는 시차 효과가 있는 UIDragPreview를 자동으로 추가합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.