본문으로 바로가기

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를 이용해 백단으로 정보를 넘겨준 후 썸네일을 표시하는 등 처리를 해주면 될 것이다.

 

그 다음으로, getRootPropsgetInputProps라는 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"));

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체