Frontend/Projects

[Toy Project 기록하기 4] 파일 업로드하기 with multer

돌잡이개발자 2023. 3. 5. 22:08



프로젝트에서 이미지 파일을 받아 업로드 하는 기능이 필요함에 따라 multer를 도입하게 됐습니다.

 

 


 

multer

파일 업로드 시 사용하는 node.js의 미들웨어이며 오직 multipart/form-data에서만 동작합니다.

 

multer 설치하기

npm install multer

 

피드를 업로드하는 화면에서 이미지를 받아 업로드 하는 기능을 추가해보려고 합니다. 

 

/feed로 post 요청 시 이미지를 업로드 할 수 있도록 라우터에 정의합니다. photoUpload 미들웨어를 통과한 뒤 uploadFeed 함수로 넘어갑니다.

// feedRouter.ts

import express from 'express';
import { photoUpload } from '../server/middlewares';

const feedRouter = express.Router();

// 한 개의 파일을 업로드 할 수 있습니다. 'photo'는 form을 통해 전송되는 파일 name 
feedRouter.post('/', photoUpload.single('photo'), uploadFeed);
// ...

 

multer의 diskStorage 엔진은 파일을 디스크에 저장하기 위한 모든 제어기능을 제공하며 destination, filename 옵션이 주어집니다. destination은 업로드 한 파일을 저장할 위치를 결정하고, filename은 폴더 안에 저장되는 파일명을 결정합니다.

// middlewares.ts

import multer from 'multer';

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/photos/');
  },
  filename: function (req, file, cb) {
    cb(null, `${Date.now()}_${file.originalname}`);
  },
});

export const photoUpload = multer({
  storage,
  // 파일 크기 제한
  limits: {
    fileSize: 3000000,
  },
});

 

이미지가 업로드되면 req.file에 나타나게 됩니다.

export const uploadFeed = async (req, res) => {
  try {
    const { title, text } = req.body;
    // req.file은 multer가 파일을 업로드 했을 때만 생성됨.
    // path는 optional하기 때문에 구조분해할당 불가
    const path = req.file?.path;

    const newFeed = await Feed.create({
      title,
      text,
      photo: path,
    });

    return res.status(200).send(newFeed);
  } catch (error) {
    return res.status(500).send({ message: DEFAULT_ERROR_MESSAGE });
  }
};

 

피드를 업로드하는 페이지에서 이미지를 업로드하는 방법에 대해 알아보겠습니다.

먼저 multipart/form-data 타입의 폼을 만들어 준 뒤, handlePhotoFileChange를 통해 이벤트 발생 시 preview에 추가해 이미지를 미리 보게 했고 form이 submit될 때 /feed로 formData를 post 요청을 보냈습니다.

export default function UploadFeed() {
  const [preview, setPreview] = useState();
  
  onst handlePhotoFileChange = async (event) => {
    const file = event.target.files[0];
    file && setPreview(URL.createObjectURL(file));
  };

  const onValid = ({
    title,
    text,
    photoFile,
  }) => {
    const formData = new FormData();
    formData.append('title', title);
    formData.append('text', text);
    photoFile && formData.append('photo', photoFile);

    return axiosInstance.post(`/feed`, formData);
  };

  return (
    <form
      method="POST"
      encType="multipart/form-data"
      onSubmit={handleSubmit(onValid)}
    >
      // ...
      <div>
        <label htmlFor="photoFile">
          사진
        </label>
        <input
          id="photoFile"
          type="file"
          accept="image/*"
          {...register('photoFile', {
            onChange: handlePhotoFileChange,
          })}
        />
      </div>
      <div>
        {preview && (
          <img src={preview} alt="preview"></img>
        )}
      </div>
      {isError && isAxiosError(error) && (
        <p>{error.response?.data.message}</p>
      )}
      <div>
        <button
          type="submit"
          disabled={isLoading}
        >
          {isLoading ? '업로드 중' : '올리기'}
        </button>
      </div>
    </form>
  );
}

 

 

🧗‍♀️ 제가 진행한 프로젝트가 궁금하다면

🔽 프론트엔드는 이곳에서 확인하실 수 있습니다.

https://github.com/Team-Madstone/doljabee-fe

 

GitHub - Team-Madstone/doljabee-fe: Climbing Community

Climbing Community. Contribute to Team-Madstone/doljabee-fe development by creating an account on GitHub.

github.com

 

🔽 백엔드는 이곳에서 확인하실 수 있습니다.

https://github.com/Team-Madstone/doljabee-be

 

GitHub - Team-Madstone/doljabee-be: Climbing Community

Climbing Community. Contribute to Team-Madstone/doljabee-be development by creating an account on GitHub.

github.com

 

참고 자료

https://www.npmjs.com/package/multer

반응형