[JS]React에서 Express로 이미지 파일 올리기(multer)
서론
페이스북과 인스타그램과 같은 SNS는 물론 지금 사용하는 블로그부터 왠만한 웹 서비스는 이미지 업로드를 지원합니다. front-end에서 React를 사용하여 Express 서버로 이미지를 전송하는 방법에 대해 알아봅시다.
Concurrently
위 글을 참조하여 React와 Express를 Concurrently를 통해 연결하는 방법을 먼저 배웁니다.
Front
yarn add axios
먼저 Client(React가 설치된 루트 디렉토리)폴더에서 ajax 통신을 위해 axios 라이브러리를 다운 받습니다.
import React, { useState } from "react";
import axios from "axios";const BASE_URL = "http://localhost:4000";export default function App() {
const [content, setContent] = useState("");
const [uploadedImg, setUploadedImg] = useState({
fileName: "",
fillPath: ""
});
기본적인 state 구조입니다. content는 리액트(또는 프론트 단)에서 이미지 데이터를 받아서 저장하고 백엔드로 보낼 State입니다.
uploadedImg State는 백엔드에서 받은 Response를 저장할 State입니다.
return (
<>
<form onSubmit={onSubmit}>
{uploadedImg ? (
<>
<img src={uploadedImg.filePath} alt="" />
<h3>{uploadedImg.fileName}</h3>
</>
) : (
""
)}
<input type="file" onChange={onChange} />
<button type="submit">Upload</button>
</form>
</>
);
위와 같이 JSX 코드를 작성합니다. uploadedImg 즉 서버로부터 반응을 받았는지 확인하고 받았다면 이미지 자체와 이미지를 보여줍니다.
const onChange = e => {
setContent(e.target.files[0]);
};
const onSubmit = e => {
e.preventDefault();
const formData = new FormData();
formData.append("img", content);
axios
.post("/upload", formData)
.then(res => {
const { fileName } = res.data;
console.log(fileName);
setUploadedImg({ fileName, filePath: `${BASE_URL}/img/${fileName}` });
alert("The file is successfully uploaded");
})
.catch(err => {
console.error(err);
});
};
이제 이벤트 코드들을 작성해줍니다. 프론트 단에서 서버로 이미지를 보낼 때에는 폼 데이터라는 객체에 담아서 보내야합니다. new FormData를 선언하고 “img” 라는 이름으로 onChange에 의해 담긴 content를 보내줍니다.
그리고 axios를 통해 받은 response는 fileName을 받아올 수 있도록 서버 코드를 잠시 후 작성하게 될 것입니다.
Back
먼저 필요한 라이브러리를 다운 받습니다.
yarn add express multer
multer 라이브러리는 프론트로부터 받은 파일에 대한 처리를 할 수 있도록 도와줍니다.
const path = require("path");
const express = require("express");
const app = express();
const multer = require("multer");
const PORT = 4000;app.use(express.static("public"));const storage = multer.diskStorage({
destination: "./public/img/",
filename: function(req, file, cb) {
cb(null, "imgfile" + Date.now() + path.extname(file.originalname));
}
});
먼저 Storage를 작성합니다. destination 옵션은 받아온 이미지를 어디에 저장할 것인지 정합니다. 만약 이러한 폴더가 없다면 multer가 이미지를 받아올때 자동으로 생성합니다.
cb는 콜백의 줄임말인데 이 부분을 통하여 이미지의 이름을 정해줍니다. 타임스탬프와 파일의 확장자명을 정해서 저장할 수 있도록 해줍니다. 이렇게 하지 않으면 같은 이름의 파일이 저장되는 경우 버그가 생깁니다.
이 방법은 로컬 서버 폴더에 이미지를 저장하게 되는데 AWS S3 Bucket 등으로 이미지 데이터를 클라우드 서버에 보관할 수 있습니다. 그 경우 이 Storage 코드를 다르게 작성해줍니다.
const upload = multer({
storage: storage,
limits: { fileSize: 1000000 }
});
다음은 업로드 옵션입니다. 어떤 storage를 사용할 것인지(로컬 또는 클라우드) 그리고 파일의 사이즈를 정합니다. 단위는 byte입니다.
app.post("/upload", upload.single("img"), function(req, res, next) {
res.send({
fileName: req.file.filename
});
});
이제 프론트의 axios로부터 post 요청을 받을 코드를 작성합니다. 프론트에서 formData에서 정해주었던 이름으로 img를 받습니다. 그리고 Storage 옵션에서 정해준 fileName을 다시 프론트로 보내줍니다.
여기서
console.log(req.file)
을 통해 여러가지 요청 결과를 확인할 수 있습니다.