React에서 올리고, 백단에서 multe-s3로 처리하는 과정에 있어서 좀 더 사용자의 편의에 있어 파일 업로드를 폴더에서 선택하는 것이 아닌 드래그 앤 드랍으로 구현해보고 싶었다.
https://github.com/react-dropzone/react-dropzone
공식 문서에 있는 예제다. 하나씩 뜯어보자.
import React, {useCallback} from 'react'
import {useDropzone} from 'react-dropzone'
function MyDropzone() {
const onDrop = useCallback(acceptedFiles => {
// Do something with the files
}, [])
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
{
isDragActive ?
<p>Drop the files here ...</p> :
<p>Drag 'n' drop some files here, or click to select files</p>
}
</div>
)
}
우선 useCallback을 통해 일어나는 일은, 업로드된 파일에 대한 정보가 acceptedFile에 담긴다는 것이다.
이를 axios나 fetch를 이용해 백단으로 정보를 넘겨준 후 썸네일을 표시하는 등 처리를 해주면 될 것이다.
그 다음으로, getRootProps와 getInputProps라는 props가 보인다.
isDragActive는 간단히, Dropzone 위에 파일이 감지되었을 때 true로 바뀌는 값이다.
getRootProps에는 dropzone의 클릭, 드래그 등 각종 이벤트에 대응하는 함수가 들어 있으며
getInputProps에는 제목 그대로 input에게 주는 속성 required 등등... 이 정의되어 있다.
다시 코드를 보면 return 하면서 속성에 해당 속서와 함수를 spreader로 뿌려놓은 것을 볼 수 있다.
한편, 파일을 업로드했을 경우 반환하는 파일과 관련된 정보는 다음과 같다.
사이즈는 byte 단위이다.
이걸 이용해서 사이즈나 name을 통해서 지정하지 않은 확장자나 일정 용량의 크기를 걸러내면 되겠죠.
그렇다면 해당 속성을 수정해보도록하자.
하이고 좀 길긴 한데 딱히 분리해봐야 복잡해져서 통째로 작성했다.
중요한건 이미지, 비디오 업로드시 axios의 헤더에 반드시 content-type을 명시해줘야 한다는 것이다.
function MyDropzone() {
const onDrop = useCallback(async (acceptedFiles) => {
// 사용자가 올린 정보를 확인해야 하므로 일단 서버로 전송합니다.
// 제목 같은 건 폼을 제출한 이후에 달아주도록 합시다.
// 폼데이터 구성
const formData = new FormData();
const config = {
header: {
"content-type": "multipart/form-data",
},
};
formData.append("file", acceptedFiles[0]);
console.log(acceptedFiles[0]);
// 배포시에는 지워줘야 합니다.
axios.defaults.baseURL = "http://localhost:5000/";
await axios.post("/api/image/upload", formData, config).then((res) => {
console.log(res);
});
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
const InputProps = {
...getInputProps(),
multiple: false,
accept: "image/gif, image/jpg, image/jpeg",
};
const RootProps = {
...getRootProps(),
};
return (
<DropZone {...RootProps} maxSize={100} multiple={false}>
<input {...InputProps} />
{isDragActive ? (
<p>이제 이미지를 놓아주세요</p>
) : (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<div style={{ fontSize: "3em", marginBottom: "5px" }}>
<i className="fas fa-file-upload"></i>
</div>
<div>이미지 드랍 or 클릭</div>
</div>
)}
</DropZone>
);
}
express 백단에서는 multer를 거쳐서 받으면 된다. single로 받아줄 때는 file로 받으면 된다.
const multer = require("multer");
const ImageUpload = multer({ dest: "uploads/" });
const uploadImageMulterMiddleware = ImageUpload.single("file");
module.exports = { uploadImageMulterMiddleware };
const express = require("express");
const { uploadImageMulterMiddleware } = require("./middleware");
const app = express();
app.post("/api/image/upload", uploadImageMulterMiddleware, (req, res) => {
console.log(req.file);
return res.json("sss");
});
app.listen(5000, () => console.log("server on : http://localhost:5000"));
'React, Next, Redux > ⚛ React.JS' 카테고리의 다른 글
React 성능 최적화 : React.memo와 useCallback, 함수형 업데이트 (0) | 2020.07.10 |
---|---|
history stack과 push/replace/go (0) | 2020.07.04 |
커스텀 Hoc 만들기 (0) | 2020.06.17 |
react-intl 적용하여 번역을 지원하는 Internationalization 하기 (0) | 2020.06.10 |
React에서 radio button 사용법 (0) | 2020.06.05 |