Drag And Drop Event

eeensu·2023년 11월 5일
0

React 실무

목록 보기
15/22
post-thumbnail

모든 HTMLElement 상속 요소는 draggable이라는 boolean 타입 속성을 제공한다.
만약 draggable속성을 적용하지 않고 드래그했을 때는 일반적으로 다음과 같이 된다.

이제 draggable 요소를 다음과 같이 적용해보자. 엘리먼트 속성의 draggable을 적용한다.

<div draggable>Information</div>

이 요소를 클릭한 체 드래그했을 때, 다음과 같이 된다. (드래그 중 캡쳐)
이로 인해 흔히 사용하는 파일을 드래그 앤 드랍하는 기능을 구현할 수 있다.

이러한 드래그 앤 드롭에 관련된 주요 이벤트는 아래와 같다.

  • dragstart
    드래그 작업이 시작될 때 발생하는 이벤트이다.

  • drag
    드래그 중에 요소가 움직일 때 발생하는 이벤트이다. 드래그 도중 계속해서 발생하며, 드래그되는 요소의 위치 변경 등을 처리할 수 있다.

  • dragenter
    드래그되는 요소가 드롭 대상 요소에 진입할 때 발생하는 이벤트이다. 드롭 대상 요소에 마우스가 올라갔을 때 발생한다.

  • dragover
    드롭 대상 요소 위에서 드래그 도중에 발생하는 이벤트이다. 드롭 대상 위로 마우스가 움직일 때 발생하며, 이 이벤트를 처리하여 드롭이 허용되는지 여부를 결정할 수 있다.

  • drop
    드래그된 요소가 드롭될 때 발생하는 이벤트이다. 드랍 이후 처리해야할 로직을 이곳에 구현하면 된다.

  • dragend
    드래그 앤 드롭 작업이 끝났을 때 발생하는 이벤트이다. 드래그가 성공적으로 완료되었든 실패했든 간에 발생한다.



react는 드래그 앤 드롭 효과와 관련하여 React.DragEvent 인터페이스(타입)을 제공한다. 이 타입에서 중요한 속성은 dataTransfer이다. 이를 통해 드래그 앤 드롭 동작 중에 데이터를 전달하거나 조작할 수 있다. 만약 파일을 드롭했을 때는 event.dataTransfer.files 속성을 통해 받아온 FileList 를 접근할 수 있다.

그리고 이러한 react 이벤트는 기본적으로 웹 브라우저 기본 구현 내용을 실행하게된다. 이 실행은 구현하려는 로직에 방해가 될 수 있기에 드래그앤 드롭 이벤트를 처리하려면 EventTarget 타입이 제공하는 preventDefault()를 실행해야한다.

const onDrag = (e: DragEvent) => {
 	e.preventDefault(); 
  
    // 로직 작성..
}



이러한 drag 이벤트를 바탕으로 유저의 이미지를 드롭시켜 UI에 그려주는 FileDrop 컴포넌트를 작성해 보자. 이미지는 2개 이상도 드롭이 가능하다.

// 이미지를 불러오는 유틸 함수
const imageFileReaderP = (file: Blob) => {

    const promise = new Promise<string>((res, rej) => {
        const fileReader = new FileReader();
        
        fileReader.onload = (e: ProgressEvent<FileReader>) => {
            const result = e.target?.result;

            if (result && typeof result === 'string') {
                res(result);
            } else {
                rej(new Error(`imageFilereaderP: can't read image File`));
            }
        }

        fileReader.readAsDataURL(file);
    });

    return promise;
}
// 컴포넌트
const ImageDrop: FC = () => {

    // 이미지 url들을 담는 state
    const [imgUrls, setImgUrls] = useState<string[]>([]);
	
  	// drogOver 이벤트를 방지해야 drop 이벤트를 발생시킬 수 있다.
    const handleDragOver = (e: DragEvent) => {
        e.preventDefault();
    }
	
    // 이미지를 불러오는 함수를 통해 받아온 Promise를 기존 state에 더해준다.
    const makeImageUrls = (files: File[]) => {
        const promise = Array.from(files).map(imageFileReaderP);

        Promise.all(promise)
            .then(urls => setImgUrls(prev => [...prev, ...urls]))
    }
	
    // 이미지를 드롭했을 때 발생하는 이벤트이다.
    const handleDrop = (e: DragEvent) => {
        e.preventDefault();

        const files = Array.from(e.dataTransfer.files);      
        files && makeImageUrls(files);
    }

    return (
        <div>
            <h1 onDragOver={handleDragOver} onDrop={handleDrop}>Drop Me!!</h1>
            <div>
                {
                    imgUrls?.map((url) => (
                        <>
                            <img 
                                style={{
                                    maxWidth: 300,
                                    maxHeight: 200,
                                    objectFit: 'contain'
                                }}  
                                src={url} 					// url로 img의 src를 지정해준다
                                alt='none' 
                            />
                            <h4>{url}</h4>					// url의 이름
                        </>
                    ))                
                }
            </div>
        </div>
    );
};

export default ImageDrop;

profile
안녕하세요! 26살 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글