심화 가이드

드래그 앤 드롭

개요

이 페이지에서는 다음 기능을 갖춘 드래그 앤 드롭 인터페이스를 빠르게 만들 수 있는 드래그 앤 드롭 디렉티브를 설명합니다.

  • 자유로운 드래그
  • 순서를 바꿀 수 있는 드래그 가능한 엘리먼트 목록 생성
  • 목록 간 드래그 가능한 엘리먼트 이동
  • 드래그 애니메이션
  • 드래그 가능한 엘리먼트의 이동을 특정 축이나 엘리먼트 안으로 제한
  • 커스텀 드래그 핸들 추가
  • 드래그 미리보기 추가
  • 커스텀 드래그 플레이스홀더 추가

전체 API 참조는 Angular CDK의 드래그 앤 드롭 API 참조 페이지를 참고하세요.

시작하기 전에

CDK 설치하기

컴포넌트 개발 도구 모음(Component Dev Kit, CDK)은 컴포넌트를 만들기 위한 기본 동작들의 집합입니다. 드래그 앤 드롭 디렉티브를 사용하려면 먼저 npm에서 @angular/cdk를 설치하세요. 터미널에서 Angular CLI로 설치할 수 있습니다.

ng add @angular/cdk

드래그 앤 드롭 불러오기

드래그 앤 드롭을 사용하려면 컴포넌트에서 필요한 디렉티브를 불러오세요.

import {Component} from '@angular/core';
import {CdkDrag} from '@angular/cdk/drag-drop';

@Component({
  selector: 'drag-drop-example',
  templateUrl: 'drag-drop-example.html',
  imports: [CdkDrag],
})
export class DragDropExample {}

드래그 가능한 엘리먼트 만들기

엘리먼트에 cdkDrag 디렉티브를 추가하면 어떤 엘리먼트든 드래그할 수 있습니다. 기본적으로 모든 드래그 가능한 엘리먼트는 자유로운 드래그를 지원합니다.

순서를 바꿀 수 있는 드래그 가능한 엘리먼트 목록 만들기

부모 엘리먼트에 cdkDropList 디렉티브를 추가하면 드래그 가능한 엘리먼트를 순서를 바꿀 수 있는 모음으로 묶을 수 있습니다. 이 디렉티브는 드래그한 엘리먼트를 놓을 수 있는 영역을 정의합니다. 드롭 목록으로 묶인 드래그 가능한 엘리먼트는 엘리먼트가 이동할 때 자동으로 재배치됩니다.

드래그 앤 드롭 디렉티브는 데이터 모델을 갱신하지 않습니다. 데이터 모델을 갱신하려면 사용자가 드래그를 마쳤을 때 발생하는 cdkDropListDropped 이벤트를 수신하고 데이터 모델을 직접 갱신하세요.

cdkDropList 인스턴스를 참조하려면 CDK_DROP_LIST 주입 토큰을 사용할 수 있습니다. 자세한 내용은 의존성 주입 가이드와 드롭 목록 주입 토큰 API를 참고하세요.

목록 간 드래그 가능한 엘리먼트 이동하기

cdkDropList 디렉티브는 연결된 드롭 목록 사이에서 드래그 가능한 엘리먼트를 이동하는 기능을 지원합니다. cdkDropList 인스턴스를 서로 연결하는 방법은 두 가지입니다.

  • cdkDropListConnectedTo 프로퍼티에 다른 드롭 목록을 지정합니다.
  • cdkDropListGroup 어트리뷰트가 있는 엘리먼트로 해당 엘리먼트들을 감쌉니다.

cdkDropListConnectedTo 디렉티브는 다른 cdkDropList를 직접 참조하거나 다른 드롭 컨테이너의 id를 참조하는 방식 모두 지원합니다.

<!-- 올바른 사용법입니다. -->
<div cdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div>
<div cdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div>

<!-- 이 방법도 올바른 사용법입니다. -->
<div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div>
<div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>

연결할 드롭 목록의 개수를 미리 알 수 없다면 cdkDropListGroup 디렉티브로 연결을 자동 설정하세요. 그룹 아래에 새로 추가되는 모든 cdkDropList는 다른 모든 목록과 자동으로 연결됩니다.

<div cdkDropListGroup>
  <!-- 이 안의 모든 목록이 서로 연결됩니다. -->
  @for (list of lists; track list) {
    <div cdkDropList></div>
  }
</div>

cdkDropListGroup 인스턴스를 참조하려면 CDK_DROP_LIST_GROUP 주입 토큰을 사용할 수 있습니다. 자세한 내용은 의존성 주입 가이드와 드롭 목록 그룹 주입 토큰 API를 참고하세요.

선택적으로 드래그 허용하기

기본적으로 사용자는 cdkDrag 엘리먼트를 한 컨테이너에서 연결된 다른 컨테이너로 옮길 수 있습니다. 어떤 엘리먼트를 컨테이너에 놓을 수 있는지 더 세밀하게 제어하려면 cdkDropListEnterPredicate를 사용하세요. 드래그 가능한 엘리먼트가 새 컨테이너에 들어올 때마다 Angular가 조건 함수를 호출합니다. 이 함수가 true 또는 false 중 무엇을 반환하는지에 따라 항목을 새 컨테이너에 넣을 수 있는지가 결정됩니다.

데이터 연결하기

cdkDragData와 cdkDropListData를 각각 설정하면 cdkDrag와 cdkDropList에 임의의 데이터를 연결할 수 있습니다. 두 디렉티브에서 발생하는 이벤트에는 이 데이터가 포함되므로, 이벤트에 바인딩하면 드래그나 드롭 상호작용이 어디에서 시작되었는지 쉽게 파악할 수 있습니다.

@for (list of lists; track list) {
  <div cdkDropList [cdkDropListData]="list" (cdkDropListDropped)="drop($event)">
    @for (item of list; track item) {
      <div cdkDrag [cdkDragData]="item"></div>
    }
  </div>
}

드래그 커스터마이징

드래그 핸들 커스터마이징

기본적으로 사용자는 cdkDrag 엘리먼트의 어느 부분을 잡아도 드래그해 이동할 수 있습니다. 핸들 엘리먼트로만 드래그하도록 제한하려면 cdkDrag 안의 엘리먼트에 cdkDragHandle 디렉티브를 추가하세요. cdkDragHandle 엘리먼트는 원하는 만큼 추가할 수 있습니다.

cdkDragHandle 인스턴스를 참조하려면 CDK_DRAG_HANDLE 주입 토큰을 사용할 수 있습니다. 자세한 내용은 의존성 주입 가이드와 드래그 핸들 주입 토큰 API를 참고하세요.

드래그 미리보기 커스터마이징

cdkDrag 엘리먼트를 드래그하면 미리보기 엘리먼트가 나타납니다. 기본 미리보기는 원본 엘리먼트의 복제본이며 사용자 커서 옆에 배치됩니다.

미리보기를 커스터마이징하려면 *cdkDragPreview로 커스텀 템플릿을 제공하세요. 엘리먼트의 내용에 대해 가정하지 않으므로 커스텀 미리보기는 드래그하는 원본 엘리먼트와 크기가 일치하지 않습니다. 드래그 미리보기의 크기를 원본 엘리먼트에 맞추려면 matchSize 입력값에 true를 전달하세요.

같은 id를 가진 엘리먼트가 페이지에 여러 개 존재하지 않도록 복제된 엘리먼트에서는 id 어트리뷰트가 제거됩니다. 따라서 해당 id를 대상으로 하는 CSS는 적용되지 않습니다.

cdkDragPreview 인스턴스를 참조하려면 CDK_DRAG_PREVIEW 주입 토큰을 사용할 수 있습니다. 자세한 내용은 의존성 주입 가이드와 드래그 미리보기 주입 토큰 API를 참고하세요.

드래그 미리보기 삽입 위치 커스터마이징

Angular는 기본적으로 위치 지정과 오버플로 문제를 피하기 위해 페이지의 <body>에 cdkDrag 미리보기를 삽입합니다. 하지만 이 경우 미리보기에 상속된 스타일이 적용되지 않으므로 적절하지 않을 수 있습니다.

cdkDrag의 cdkDragPreviewContainer 입력값으로 Angular가 미리보기를 삽입할 위치를 변경할 수 있습니다. 사용 가능한 값은 다음과 같습니다.

값 설명 장점 단점
global 기본값입니다. Angular가 또는 가장 가까운 섀도 루트에 미리보기를 삽입합니다. 미리보기가 z-index나 overflow: hidden의 영향을 받지 않습니다. :nth-child 선택자와 flex 레이아웃에도 영향을 주지 않습니다. 상속된 스타일을 유지하지 않습니다.
parent Angular가 드래그하는 엘리먼트의 부모 안에 미리보기를 삽입합니다. 미리보기가 드래그하는 엘리먼트와 같은 스타일을 상속합니다. overflow: hidden에 의해 미리보기가 잘리거나 z-index 때문에 다른 엘리먼트 아래에 놓일 수 있습니다. 또한 :nth-child 선택자와 일부 flex 레이아웃에 영향을 줄 수 있습니다.
ElementRef 또는 HTMLElement Angular가 지정한 엘리먼트에 미리보기를 삽입합니다. 미리보기가 지정한 컨테이너 엘리먼트의 스타일을 상속합니다. overflow: hidden에 의해 미리보기가 잘리거나 z-index 때문에 다른 엘리먼트 아래에 놓일 수 있습니다. 또한 :nth-child 선택자와 일부 flex 레이아웃에 영향을 줄 수 있습니다.

또는 값이 global이나 parent인 경우, CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 previewContainer를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

드래그 플레이스홀더 커스터마이징

cdkDrag 엘리먼트를 드래그하는 동안 디렉티브는 엘리먼트를 놓을 위치를 보여주는 플레이스홀더 엘리먼트를 생성합니다. 기본 플레이스홀더는 드래그하는 엘리먼트의 복제본입니다. *cdkDragPlaceholder 디렉티브로 커스텀 플레이스홀더를 사용할 수 있습니다.

cdkDragPlaceholder 인스턴스를 참조하려면 CDK_DRAG_PLACEHOLDER 주입 토큰을 사용할 수 있습니다. 자세한 내용은 의존성 주입 가이드와 드래그 플레이스홀더 주입 토큰 API를 참고하세요.

드래그 루트 엘리먼트 커스터마이징

드래그할 수 있게 만들려는 엘리먼트에 직접 접근할 수 없다면 cdkDragRootElement 어트리뷰트를 설정하세요.

이 어트리뷰트는 선택자를 받아 일치하는 엘리먼트를 찾을 때까지 DOM의 상위 방향으로 탐색합니다. 일치하는 엘리먼트를 찾으면 해당 엘리먼트를 드래그할 수 있게 됩니다. 대화상자를 드래그할 수 있게 만드는 등의 경우에 유용합니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 rootElementSelector를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

드래그 가능한 엘리먼트의 DOM 위치 설정하기

기본적으로 cdkDropList 안에 있지 않은 cdkDrag 엘리먼트는 사용자가 직접 옮길 때만 일반적인 DOM 위치에서 이동합니다. 엘리먼트의 위치를 명시적으로 설정하려면 cdkDragFreeDragPosition 입력값을 사용하세요. 사용자가 다른 페이지로 이동했다가 돌아왔을 때 드래그 가능한 엘리먼트의 위치를 복원하는 것이 대표적인 사용 사례입니다.

이동 범위를 엘리먼트 안으로 제한하기

사용자가 cdkDrag 엘리먼트를 다른 엘리먼트 밖으로 드래그하지 못하게 하려면 cdkDragBoundary 어트리뷰트에 CSS 선택자를 전달하세요. 이 어트리뷰트는 선택자를 받아 일치하는 엘리먼트를 찾을 때까지 DOM의 상위 방향으로 탐색합니다. 일치하는 엘리먼트를 찾으면 해당 엘리먼트가 드래그 가능한 엘리먼트가 벗어날 수 없는 경계가 됩니다. cdkDrag가 cdkDropList 안에 있는 경우에도 cdkDragBoundary를 사용할 수 있습니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 boundaryElement를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

이동을 특정 축으로 제한하기

기본적으로 cdkDrag는 모든 방향으로 자유롭게 이동할 수 있습니다. 드래그를 특정 축으로 제한하려면 cdkDrag의 cdkDragLockAxis를 "x" 또는 "y"로 설정하세요. cdkDropList 안의 여러 드래그 가능한 엘리먼트에 같은 제한을 적용하려면 cdkDropList에 cdkDropListLockAxis를 설정하세요.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 lockAxis를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

드래그 시작 지연하기

기본적으로 사용자가 cdkDrag 위에서 포인터를 누르면 드래그 과정이 시작됩니다. 터치 기기에서 전체 화면을 차지하는 드래그 가능한 엘리먼트처럼, 사용자가 페이지를 스크롤하다가 실수로 드래그 이벤트를 발생시킬 수 있는 경우에는 이 동작이 적절하지 않을 수 있습니다.

cdkDragStartDelay 입력값으로 드래그 시작을 지연할 수 있습니다. 이 입력값은 사용자가 지정된 밀리초만큼 포인터를 누른 상태를 유지한 후에 엘리먼트 드래그를 시작하도록 합니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 dragStartDelay를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

드래그 비활성화하기

특정 항목의 드래그 활성화 여부를 바꾸려면 cdkDrag 항목의 cdkDragDisabled 입력값을 true 또는 false로 설정하세요. cdkDropList의 cdkDropListDisabled 입력값으로 목록 전체를 비활성화할 수 있습니다. cdkDragHandle의 cdkDragHandleDisabled로 특정 핸들만 비활성화할 수도 있습니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 draggingDisabled를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

정렬 커스터마이징

목록 방향

기본적으로 cdkDropList 디렉티브는 목록이 세로 방향이라고 가정합니다. cdkDropListOrientation 프로퍼티를 horizontal로 설정하면 이를 변경할 수 있습니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 listOrientation을 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

목록 줄바꿈

기본적으로 cdkDropList는 CSS transform으로 드래그 가능한 엘리먼트를 이동해 정렬합니다. 이 방식은 정렬에 애니메이션을 적용할 수 있어 사용자 경험을 개선합니다. 다만 드롭 목록이 세로 또는 가로 중 한 방향으로만 동작한다는 단점이 있습니다.

정렬 가능한 목록이 여러 줄로 이어져야 한다면 cdkDropListOrientation 어트리뷰트를 mixed로 설정할 수 있습니다. 그러면 목록은 DOM에서 엘리먼트를 실제로 이동하는 다른 정렬 전략을 사용합니다. 하지만 이 경우 정렬 동작에 애니메이션을 적용할 수 없습니다.

선택적으로 정렬 허용하기

기본적으로 cdkDrag 엘리먼트는 cdkDropList 안의 어느 위치로든 정렬할 수 있습니다. 이 동작을 변경하려면 함수를 받는 cdkDropListSortPredicate 어트리뷰트를 설정하세요. 드래그 가능한 엘리먼트를 드롭 목록의 새 인덱스로 이동하려고 할 때마다 조건 함수를 호출합니다. 함수가 true를 반환하면 항목을 새 인덱스로 이동하고, 그렇지 않으면 현재 위치를 유지합니다.

정렬 비활성화하기

드래그 가능한 엘리먼트를 한 cdkDropList에서 다른 목록으로 옮길 수는 있지만, 원본 목록 안에서는 사용자가 순서를 바꾸지 못하게 해야 하는 경우가 있습니다. 이때는 cdkDropListSortingDisabled 어트리뷰트를 추가해 cdkDropList 안의 드래그 가능한 엘리먼트가 정렬되지 않게 하세요. 이렇게 하면 엘리먼트를 새로운 유효한 위치로 드래그하지 않았을 때 원본 목록의 처음 위치가 유지됩니다.

또는 CDK_DRAG_CONFIG 주입 토큰을 수정해 설정의 sortingDisabled를 갱신할 수 있습니다. 자세한 내용은 의존성 주입 가이드, 드래그 설정 주입 토큰 API, 드래그 앤 드롭 설정 API를 참고하세요.

목록 간 항목 복사하기

기본적으로 항목을 한 목록에서 다른 목록으로 드래그하면 원래 목록에서 제거되어 이동합니다. 하지만 디렉티브를 설정해 원본 목록의 항목을 유지하면서 복사할 수도 있습니다.

복사를 활성화하려면 cdkDropListHasAnchor 입력값을 설정하세요. 그러면 cdkDropList는 항목과 함께 이동하지 않고 원래 컨테이너에 남아 있는 "앵커" 엘리먼트를 생성합니다. 사용자가 항목을 원래 컨테이너로 다시 옮기면 앵커는 자동으로 제거됩니다. .cdk-drag-anchor CSS 클래스로 앵커 엘리먼트의 스타일을 지정할 수 있습니다.

cdkDropListHasAnchor와 cdkDropListSortingDisabled를 함께 사용하면 원본 목록의 순서를 바꾸지 않으면서 항목을 복사할 수 있는 목록을 만들 수 있습니다(예: 상품 목록과 장바구니).

애니메이션 커스터마이징

드래그 앤 드롭은 다음 두 동작에 애니메이션을 지원합니다.

  • 목록 안에서 드래그 가능한 엘리먼트 정렬
  • 사용자가 놓은 위치에서 목록 안의 최종 위치까지 드래그 가능한 엘리먼트 이동

애니메이션을 설정하려면 transform 프로퍼티를 대상으로 하는 CSS 트랜지션을 정의하세요. 애니메이션에 사용할 수 있는 클래스는 다음과 같습니다.

CSS 클래스 이름 트랜지션을 추가했을 때의 효과
.cdk-drag 드래그 가능한 엘리먼트가 정렬되는 동안 애니메이션을 적용합니다.
.cdk-drag-animating 드래그 가능한 엘리먼트가 놓인 위치에서 cdkDropList 안의 최종 위치로 이동하는 애니메이션을 적용합니다.

이 CSS 클래스는 드래그 동작이 끝났을 때만 cdkDrag 엘리먼트에 적용됩니다.

스타일 지정하기

cdkDrag와 cdkDropList 디렉티브는 기능 동작에 꼭 필요한 스타일만 적용합니다. 애플리케이션에서는 다음 CSS 클래스를 대상으로 스타일을 커스터마이징할 수 있습니다.

CSS 클래스 이름 설명
.cdk-drop-list cdkDropList 컨테이너 엘리먼트의 선택자입니다.
.cdk-drag cdkDrag 엘리먼트의 선택자입니다.
.cdk-drag-disabled 비활성화된 cdkDrag 엘리먼트의 선택자입니다.
.cdk-drag-handle cdkDragHandle의 호스트 엘리먼트 선택자입니다.
.cdk-drag-preview 드래그 미리보기 엘리먼트의 선택자입니다. 사용자가 정렬 가능한 목록에서 엘리먼트를 드래그할 때 커서 옆에 나타나는 엘리먼트입니다.

*cdkDragPreview의 커스텀 템플릿으로 변경하지 않는 한 드래그하는 엘리먼트와 똑같이 보입니다.
.cdk-drag-placeholder 드래그 플레이스홀더 엘리먼트의 선택자입니다. 드래그 동작이 끝난 후 드래그 가능한 엘리먼트가 놓일 자리에 표시되는 엘리먼트입니다.

cdkDragPlaceholder 디렉티브로 변경하지 않는 한 정렬하는 엘리먼트와 똑같이 보입니다.
.cdk-drop-list-dragging 현재 드래그 중인 엘리먼트가 있는 cdkDropList 컨테이너 엘리먼트의 선택자입니다.
.cdk-drop-list-disabled 비활성화된 cdkDropList 컨테이너 엘리먼트의 선택자입니다.
.cdk-drop-list-receiving 연결된 드롭 목록에서 현재 드래그 중인 엘리먼트를 받을 수 있는 cdkDropList 컨테이너 엘리먼트의 선택자입니다.
.cdk-drag-anchor cdkDropListHasAnchor를 활성화했을 때 생성되는 앵커 엘리먼트의 선택자입니다. 이 엘리먼트는 드래그한 항목의 시작 위치를 나타냅니다.

스크롤 가능한 컨테이너에서 드래그하기

드래그 가능한 항목이 스크롤 가능한 컨테이너(예: overflow: auto가 지정된 div) 안에 있다면, 해당 컨테이너에 cdkScrollable 디렉티브가 있어야 자동 스크롤이 동작합니다. 이 디렉티브가 없으면 CDK는 드래그하는 동안 컨테이너의 스크롤 동작을 감지하거나 제어할 수 없습니다.

다른 컴포넌트와 통합하기

CDK의 드래그 앤 드롭 기능은 다양한 컴포넌트와 통합할 수 있습니다. 대표적인 사용 사례로는 정렬 가능한 MatTable 컴포넌트와 정렬 가능한 MatTabGroup 컴포넌트가 있습니다.