React에서 HTML 드래그 앤 드롭 API를 사용하는 방법

게시 됨: 2022-03-10
빠른 요약 ↬ 이 튜토리얼에서는 파일 및 이미지 업로드를 위한 React 드래그 앤 드롭 구성 요소를 빌드합니다. 이 과정에서 HTML 드래그 앤 드롭 API에 대해 알아보겠습니다. 또한 React 기능 구성 요소에서 상태를 관리하기 위해 useReducer 후크를 사용하는 방법도 배웁니다.

드래그 앤 드롭 API는 HTML의 가장 멋진 기능 중 하나입니다. 웹 브라우저에서 끌어서 놓기 기능을 구현하는 데 도움이 됩니다.

현재 컨텍스트에서는 브라우저 외부에서 파일을 드래그합니다. 파일을 드롭하면 목록에 넣고 이름을 표시합니다. 파일을 가지고 있으면 파일에 대해 다른 작업을 수행할 수 있습니다(예: 클라우드 서버에 업로드).

이 튜토리얼에서는 React 애플리케이션에서 드래그 앤 드롭 동작을 구현하는 방법에 중점을 둘 것입니다. 간단한 JavaScript 구현이 필요한 경우 먼저 Joseph Zimmerman이 작성한 훌륭한 자습서인 "바닐라 JavaScript로 끌어서 놓기 파일 업로더를 만드는 방법"을 읽고 싶을 것입니다.

dragenter , dragleave , dragoverdrop 이벤트

8개의 서로 다른 끌어서 놓기 이벤트가 있습니다. 각각은 끌어서 놓기 작업의 다른 단계에서 실행됩니다. 이 튜토리얼에서는 항목을 드롭 영역에 놓을 때 발생하는 네 가지( dragenter , dragleave , dragoverdrop )에 중점을 둘 것입니다.

  1. dragenter 이벤트는 드래그된 항목이 유효한 놓기 대상에 들어갈 때 발생합니다.
  2. dragleave 이벤트는 드래그된 항목이 유효한 놓기 대상을 벗어날 때 발생합니다.
  3. dragover 이벤트는 드래그된 항목이 유효한 놓기 대상 위로 드래그될 때 발생합니다. (수백 밀리초마다 실행됩니다.)
  4. drop 이벤트는 항목이 유효한 드롭 대상에 떨어질 때 발생합니다.

ondragoverondrop 이벤트 핸들러 속성을 정의하여 모든 HTML 요소를 유효한 놓기 대상으로 전환할 수 있습니다.

MDN 웹 문서에서 8가지 이벤트에 대해 모두 배울 수 있습니다.

점프 후 더! 아래에서 계속 읽기 ↓

React의 드래그 앤 드롭 이벤트

시작하려면 다음 URL에서 튜토리얼 리포지토리를 복제하세요.

 https://github.com/chidimo/react-dnd.git

01-start 브랜치를 확인하세요. yarn 도 설치되어 있는지 확인하십시오. yarnpkg.com에서 받을 수 있습니다.

그러나 원하는 경우 새 React 프로젝트를 만들고 App.js 의 내용을 아래 코드로 바꾸세요.

 import React from 'react'; import './App.css'; function App() { return ( <div className="App"> <h1>React drag-and-drop component</h1> </div> ); } export default App;

또한 App.css 의 내용을 아래 CSS 스타일로 교체합니다.

 .App { margin: 2rem; text-align: center; } h1 { color: #07F; } .drag-drop-zone { padding: 2rem; text-align: center; background: #07F; border-radius: 0.5rem; box-shadow: 5px 5px 10px #C0C0C0; } .drag-drop-zone p { color: #FFF; } .drag-drop-zone.inside-drag-area { opacity: 0.7; } .dropped-files li { color: #07F; padding: 3px; text-align: left; font-weight: bold; }

리포지토리를 복제한 경우 다음 명령을 순서대로 실행하여 앱을 시작합니다.

 yarn # install dependencies yarn start # start the app

다음 단계는 끌어서 놓기 구성 요소를 만드는 것입니다. src/ 폴더 안에 DragAndDrop.js 파일을 생성합니다. 파일 내에 다음 함수를 입력합니다.

 import React from 'react'; const DragAndDrop = props => { const handleDragEnter = e => { e.preventDefault(); e.stopPropagation(); }; const handleDragLeave = e => { e.preventDefault(); e.stopPropagation(); }; const handleDragOver = e => { e.preventDefault(); e.stopPropagation(); }; const handleDrop = e => { e.preventDefault(); e.stopPropagation(); }; return ( <div className={'drag-drop-zone'} onDrop={e => handleDrop(e)} onDragOver={e => handleDragOver(e)} onDragEnter={e => handleDragEnter(e)} onDragLeave={e => handleDragLeave(e)} > <p>Drag files here to upload</p> </div> ); }; export default DragAndDrop;

반환 div 에서 포커스 HTML 이벤트 핸들러 속성을 정의했습니다. 순수한 HTML 과의 유일한 차이점은 낙타 모양이라는 것을 알 수 있습니다.

onDragOveronDrop 이벤트 핸들러 속성을 정의했기 때문에 div 는 이제 유효한 놓기 대상입니다.

또한 이러한 이벤트를 처리하는 함수를 정의했습니다. 이러한 각 처리기 함수는 이벤트 개체를 인수로 받습니다.

각 이벤트 핸들러에 대해 preventDefault() 를 호출하여 브라우저가 기본 동작을 실행하지 못하도록 합니다. 기본 브라우저 동작은 드롭된 파일을 여는 것입니다. 또한 이벤트가 자식 요소에서 부모 요소로 전파되지 않도록 stopPropagation() 을 호출합니다.

DragAndDrop 구성 요소를 App 구성 요소로 가져와 제목 아래에 렌더링합니다.

 <div className="App"> <h1>React drag-and-drop component</h1> <DragAndDrop /> </div>

이제 브라우저에서 구성 요소를 보면 아래 이미지와 같은 내용이 표시되어야 합니다.

드롭존
div 를 드롭 영역으로 변환(큰 미리보기)

repo를 팔로우하는 경우 해당 분기는 02-start-dragndrop

useReducer Hook으로 상태 관리하기

다음 단계는 각 이벤트 핸들러에 대한 로직을 작성하는 것입니다. 그렇게 하기 전에 삭제된 파일을 추적하는 방법을 고려해야 합니다. 여기에서 상태 관리에 대해 생각하기 시작합니다.

끌어서 놓기 작업 중에 다음 상태를 추적합니다.

  1. dropDepth
    이것은 정수가 됩니다. 우리는 드롭 존에 얼마나 많은 레벨이 있는지 추적하는 데 사용할 것입니다. 나중에 그림으로 설명하겠습니다. ( 저를 위해 빛을 비춰준 Egor Egorov에게 감사드립니다! )
  2. inDropZone
    이것은 부울이 됩니다. 우리는 이것을 사용하여 드롭 존 안에 있는지 여부를 추적합니다.
  3. FileList
    이것은 목록이 될 것입니다. 이를 사용하여 드롭 영역에 드롭된 파일을 추적합니다.

상태를 처리하기 위해 React는 useStateuseReducer 후크를 제공합니다. 우리는 상태가 이전 상태에 의존하는 상황을 다룰 것이라는 점을 감안할 때 useReducer 후크를 선택할 것입니다.

useReducer 후크는 (state, action) => newState 유형의 감속기를 허용하고 dispatch 메서드와 쌍을 이루는 현재 상태를 반환합니다.

React 문서에서 useReducer대해 자세히 읽을 수 있습니다 .

App 구성 요소 내부( return 문 앞)에 다음 코드를 추가합니다.

 ... const reducer = (state, action) => { switch (action.type) { case 'SET_DROP_DEPTH': return { ...state, dropDepth: action.dropDepth } case 'SET_IN_DROP_ZONE': return { ...state, inDropZone: action.inDropZone }; case 'ADD_FILE_TO_LIST': return { ...state, fileList: state.fileList.concat(action.files) }; default: return state; } }; const [data, dispatch] = React.useReducer( reducer, { dropDepth: 0, inDropZone: false, fileList: [] } ) ...

useReducer 후크는 리듀서와 초기 상태라는 두 가지 인수를 허용합니다. 현재 상태와 상태를 업데이트할 dispatch 함수를 반환합니다. 상태는 type 과 선택적 페이로드가 포함된 작업을 전달하여 업데이트됩니다. 구성 요소 상태에 대한 업데이트는 작업 유형의 결과로 case 문에서 반환된 내용에 따라 다릅니다. (여기서 초기 상태는 object 라는 점에 유의하십시오.)

각 상태 변수에 대해 해당 케이스 문을 정의하여 업데이트했습니다. 업데이트는 useReducer 에서 반환된 dispatch 함수를 호출하여 수행됩니다.

이제 App.js 파일에 있는 DragAndDrop 구성 요소에 datadispatch 하고 props 으로 전달합니다.

 <DragAndDrop data={data} dispatch={dispatch} />

DragAndDrop 구성 요소의 상단에서 props 의 두 값에 모두 액세스할 수 있습니다.

 const { data, dispatch } = props;

repo를 따르는 경우 해당 분기는 03-define-reducers 입니다.

이벤트 핸들러의 논리를 완성해 보겠습니다. 줄임표는 두 줄을 나타냅니다.

 e.preventDefault() e.stopPropagation() const handleDragEnter = e => { ... dispatch({ type: 'SET_DROP_DEPTH', dropDepth: data.dropDepth + 1 }); }; const handleDragLeave = e => { ... dispatch({ type: 'SET_DROP_DEPTH', dropDepth: data.dropDepth - 1 }); if (data.dropDepth > 0) return dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: false }) };

다음 그림에는 드롭 영역 A와 B가 중첩되어 있습니다. A가 관심 영역입니다. 여기에서 끌어서 놓기 이벤트를 수신하려고 합니다.

ondragenter 및 ondragleave 이벤트의 그림
ondragenterondragleave 이벤트 그림(큰 미리보기)

드롭 영역으로 드래그할 때 경계에 도달할 때마다 ondragenter 이벤트가 시작됩니다. 이것은 경계 A-inB-in 발생합니다. 영역에 들어가고 있으므로 dropDepth 를 증가시킵니다.

마찬가지로, 드롭 영역 밖으로 드래그할 때 경계에 도달할 때마다 ondragleave 이벤트가 시작됩니다. 이것은 경계 A-outB-out 에서 발생합니다. 영역을 떠나기 때문에 dropDepth 값을 줄입니다. 경계 B-out 에서 inDropZonefalse 로 설정하지 않았음을 주목하십시오. 이것이 dropDepth를 확인하고 0 보다 큰 dropDepth 함수에서 반환하기 위해 이 라인이 있는 이유입니다.

 if (data.dropDepth > 0) return

이는 ondragleave 이벤트가 발생하더라도 여전히 영역 A 안에 있기 때문입니다. inDropZonefalse 로 설정한 것은 A-out 을 치고 dropDepth 가 이제 0 이 된 후에야 가능합니다. 이 시점에서 우리는 모든 드롭 영역을 떠났습니다.

 const handleDragOver = e => { ... e.dataTransfer.dropEffect = 'copy'; dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: true }); };

이 이벤트가 발생할 때마다 inDropZonetrue 로 설정합니다. 이것은 우리가 드롭 존 안에 있음을 알려줍니다. 또한 dataTransfer 객체의 dropEffectcopy 로 설정합니다. Mac에서는 드롭 영역에서 항목을 드래그할 때 녹색 더하기 기호를 표시하는 효과가 있습니다.

 const handleDrop = e => { ... let files = [...e.dataTransfer.files]; if (files && files.length > 0) { const existingFiles = data.fileList.map(f => f.name) files = files.filter(f => !existingFiles.includes(f.name)) dispatch({ type: 'ADD_FILE_TO_LIST', files }); e.dataTransfer.clearData(); dispatch({ type: 'SET_DROP_DEPTH', dropDepth: 0 }); dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: false }); } };

e.dataTransfer.files 를 사용하여 삭제된 파일에 액세스할 수 있습니다. 값은 배열과 유사한 객체이므로 배열 확산 구문을 사용하여 JavaScript 배열로 변환합니다.

이제 파일 배열에 파일을 추가하기 전에 파일이 하나 이상 있는지 확인해야 합니다. 또한 이미 fileList 에 있는 파일을 포함하지 않도록 합니다. dataTransfer 객체는 다음 드래그 앤 드롭 작업을 준비하기 위해 지워집니다. dropDepthinDropZone 값도 재설정합니다.

DragAndDrop 구성 요소에서 divclassName 을 업데이트합니다. 이것은 data.inDropZone 의 값에 따라 divclassName 을 조건부로 변경합니다.

 <div className={data.inDropZone ? 'drag-drop-zone inside-drag-area' : 'drag-drop-zone'} ... > <p>Drag files here to upload</p> </div>

data.fileList 를 통해 매핑하여 App.js 의 파일 목록을 렌더링합니다.

 <div className="App"> <h1>React drag-and-drop component</h1> <DragAndDrop data={data} dispatch={dispatch} /> <ol className="dropped-files"> {data.fileList.map(f => { return ( <li key={f.name}>{f.name}</li> ) })} </ol> </div>

이제 일부 파일을 드롭 영역으로 끌어다 놓으십시오. 놓기 영역에 inside-drag-area 클래스가 활성화되기 때문에 배경이 덜 불투명해지는 것을 볼 수 있습니다.

드롭 영역 내에서 파일을 해제하면 드롭 영역 아래에 파일 이름이 나열되는 것을 볼 수 있습니다.

드래그오버 중 낮은 불투명도를 표시하는 드롭 영역
드래그오버 중 낮은 불투명도를 표시하는 드롭 영역(큰 미리보기)
드롭 영역에 드롭된 파일 목록
드롭 영역에 드롭된 파일 목록(큰 미리보기)

이 튜토리얼의 전체 버전은 04-finish-handlers 분기에 있습니다.

결론

HTML 드래그 앤 드롭 API를 사용하여 React에서 파일 업로드를 처리하는 방법을 살펴보았습니다. 또한 useReducer 후크로 상태를 관리하는 방법을 배웠습니다. 파일 handleDrop 기능을 확장할 수 있습니다. 예를 들어 원하는 경우 파일 크기를 제한하기 위해 다른 검사를 추가할 수 있습니다. 이것은 기존 파일을 확인하기 전이나 후에 올 수 있습니다. 드래그 앤 드롭 기능에 영향을 주지 않고 드롭 영역을 클릭 가능하게 만들 수도 있습니다.

자원

  • "Hooks API 참조: useReducer ", React Docs
  • "HTML 드래그 앤 드롭 API", MDN 웹 문서
  • "DOM을 사용한 웹 및 XML 개발의 예", MDN 웹 문서
  • "바닐라 자바스크립트로 드래그 앤 드롭 파일 업로더를 만드는 방법", Joseph Zimmerman, Smashing Magazine
  • "React에서 간단한 드래그 앤 드롭 파일 업로드", Egor Egorov, Medium