생각해보니 드래그앤 드랍을 제대로 구현 해본적이 없다.
그래서 이번 기회에 이렇게라도 한 번 간단하게 구현이라도 해봐야 할 듯 해서. 진행해본다.
이미지만 드래그앤 드랍해서 업로드 하도록 만들어보자.
여러개도 궁금하면 이걸보자.
React 이미지 끌어서 놓기, 드래그 앤 드랍, 틀 만들기2(이미지 여러개 끌어서 놓기)
javascript에서 drag 어쩌고 하는 코드가 있어서 사용해봤다.
onDragOver()
onDragLeave()
onDrop()
을 사용해봤다.
우선 onDragOver() 이 녀석을 드래그앤 드랍을 해줄 곳에 설정해줘야 이미지 드래그 앤 드랍이 가능했다.
안 그러면 브라우저가 이미지를 새 탭에서 켜는 현상이 생겼다.
onDragLeave() 이 녀석은 드래그된 항목이 요소를 벗어날 때 발생한다고 하는데, 여러 항목에서 드래그앤 드랍을 진행할 때 유용하겠지만, 이번에는 onDragOver()에 사용되는 boolean 변수값을 조정하기 위해 사용했다.
onDrop() 이 녀석은 요소를 drop했을 때 작동 시킬때 어떻게 작동 시킬 것인지 하는 녀석이다. 여기에 내가 이미지를 넣을지 파일 크기는 얼마짜리인지 등을 작성해주면 된다.
난 그래서 이제 이것들을 활용해볼 것이다.
우선 사용할 변수를 먼저 작성해줬다.
const [preview, setPreview] = useState('default/sampleimg.png')
const [isDragOver, setIsDragOver] = useState(false)
const [file, setFile] = useState(null)
const [fileSize, setFileSize] = useState(null)
const [fileUnit, setFileUnit] = useState('KB')
const allowFileTypes = [
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
]
style은 틀을 잡기 위한 것이라 inline 방식으로 사용했다.
우선 사용되는 함수는 아래처럼 해주었다.
위에서 말한 순서대로 실행시킬 함수를 작성해주었다.
handleDrop을 통해 10MB 이하의 파일만 등록하게 하고, 또한 이미지인지 아닌지를 판단해서 이미지일 때만 파일을 업로드 시킬 수 있도록 준비했다.
const handleDragOver = (event) => {
event.preventDefault()
setIsDragOver(true)
}
const handleDragLeave = () => {
setIsDragOver(false)
}
const handleDrop = (event) => {
event.preventDefault()
setIsDragOver(false)
// 1KB 2**10 1MB는 2**20 바이트 1GB는 2**30
if(event.dataTransfer.files[0].size <= 10 * 2**20){
if(allowFileTypes.includes(event.dataTransfer.files[0].type)){
setFile(event.dataTransfer.files[0])
}else{
alert('이미지 파일만 업로드 가능합니다.')
}
console.log(event.dataTransfer.files[0].type)
}else{
alert('10MB를 초과한 파일은 불가합니다.')
}
}
<div>
<div
onDragOver={(e) => {handleDragOver(e)}}
onDragLeave={() => {handleDragLeave()}}
onDrop={(e) => {handleDrop(e)}}
style={{
border: isDragOver ? '2px dashed blue' : '2px dashed gray',
}}
>
{file ? (
<div className='' style={{width: "400px", height: '400px', display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<img src={URL.createObjectURL(file)} alt="Uploaded" width="200" />
</div>
) : (
<div className='' style={{width: "400px", height: '400px', display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<img src={preview} alt="이미지" className='img_box' width="200" height="200"/>
<p>이미지를 끌어서 올려주세요.</p>
</div>
)}
</div>
</div>
위처럼만 해도 파일은 올라간다.
하.지.만. 파일만 올라간다고 사용자가 쓸 수는 없으니, 파일이 제대로 올라갔는지 이름 확장자명과 파일크기 그리고, 취소 버튼 upload버튼까지 생성해주자.
위에서의 코드에서
{ file ?
<div>
<span>{file.name}</span> <span>{fileSize} {fileUnit}</span> <button onClick={() => {cancelFile()}}>X</button>
</div>
: <></>
}
<button onClick={() => {handleUpload()}}>Upload</button>
를 추가시켜주자.
이러면 추가시킨 파일이름과 확장자, 파일크기 그리 취소 버튼과 upload 버튼이 생긴다.
이를 또 표현할 수 있게 함수를 만들어주자.
handleUpload()에서는 실제 back과 연동되거나 db와 연동될 수 있도록 진행하면 될 것이다.
handleFileSize()는 업로드한 file의 크기를 계산해주기 위해서 작성했다. 바이트로 file.size에서 표기되는데, 이를 사용자가 보기 편하도록, KB나 MB로 변경하는 단계이다.
cancelFile()은 등록한 file을 취소하기 위한 코드이다.
그리고 file에 변화가 생겼을 때 마다 확인할 수 있게 useEffect를 활용해줬다.
const handleUpload = () => {
// 파일 업로드 로직 구현
if(file){
console.log('업로드한 파일:', file)
alert(file.name)
}else{
alert('파일을 넣은 후 진행하세요.')
}
}
const handleFileSize = (file) => {
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB']
let i = 0
let thisSize = file.size
while(thisSize >= 1024 && i < UNITS.length - 1){
thisSize /= 1024
i ++
}
if(i <= 1){
setFileSize(Math.ceil((thisSize)))
}else{
setFileSize(Math.ceil((thisSize) * 100) / 100)
}
setFileUnit(UNITS[i])
}
const cancelFile = () => {
if(file){
setFile(null)
setFileSize(null)
setFileUnit(null)
}
}
useEffect(() => {
if(file){
handleFileSize(file)
}
}, [file])
요롷코롬 하니 잘 작동하더라.
import { useEffect, useState } from 'react'
import './App.css'
function App() {
const [preview, setPreview] = useState('default/sampleimg.png')
const [isDragOver, setIsDragOver] = useState(false)
const [file, setFile] = useState(null)
const [fileSize, setFileSize] = useState(null)
const [fileUnit, setFileUnit] = useState('KB')
const allowFileTypes = [
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
]
const handleDragOver = (event) => {
event.preventDefault()
setIsDragOver(true)
}
const handleDragLeave = () => {
setIsDragOver(false)
}
const handleDrop = (event) => {
event.preventDefault()
setIsDragOver(false)
// 1KB 2**10 1MB는 2**20 바이트 1GB는 2**30
if(event.dataTransfer.files[0].size <= 10 * 2**20){
if(allowFileTypes.includes(event.dataTransfer.files[0].type)){
setFile(event.dataTransfer.files[0])
}else{
alert('이미지 파일만 업로드 가능합니다.')
}
console.log(event.dataTransfer.files[0].type)
}else{
alert('10MB를 초과한 파일은 불가합니다.')
}
}
const handleUpload = () => {
// 파일 업로드 로직 구현
if(file){
console.log('업로드한 파일:', file)
alert(file.name)
}else{
alert('파일을 넣은 후 진행하세요.')
}
}
const handleFileSize = (file) => {
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB']
let i = 0
let thisSize = file.size
while(thisSize >= 1024 && i < UNITS.length - 1){
thisSize /= 1024
i ++
}
if(i <= 1){
setFileSize(Math.ceil((thisSize)))
}else{
setFileSize(Math.ceil((thisSize) * 100) / 100)
}
setFileUnit(UNITS[i])
}
const cancelFile = () => {
if(file){
setFile(null)
setFileSize(null)
setFileUnit(null)
}
}
useEffect(() => {
if(file){
handleFileSize(file)
}
}, [file])
return (
<div>
<div
onDragOver={(e) => {handleDragOver(e)}}
onDragLeave={() => {handleDragLeave()}}
onDrop={(e) => {handleDrop(e)}}
style={{
border: isDragOver ? '2px dashed blue' : '2px dashed gray',
}}
>
{file ? (
<div className='' style={{width: "400px", height: '400px', display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<img src={URL.createObjectURL(file)} alt="Uploaded" width="200" />
</div>
) : (
<div className='' style={{width: "400px", height: '400px', display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<img src={preview} alt="이미지" className='img_box' width="200" height="200"/>
<p>이미지를 끌어서 올려주세요.</p>
</div>
)}
</div>
{ file ?
<div>
<span>{file.name}</span> <span>{fileSize} {fileUnit}</span> <button onClick={() => {cancelFile()}}>X</button>
</div>
: <></>
}
<button onClick={() => {handleUpload()}}>Upload</button>
</div>
)
}
export default App