사용자가 이미지를 업로드하고, 그 이미지를 다시 사용자에게 제공하는 기능을 만들어보려 한다.
multer라는 모듈을 사용할 예정이다. express에서 파일 업로드 시 가장 많이들 사용하는 모듈이다.
multer 공식 문서
https://www.npmjs.com/package/multer
$ npm multer --save
공식문서에는, form 태그로 입력을 받을 때, enctype="multipart/form-data"를 반드시 설정해 주라고 나와 있다.
내 테스트 템플릿은 아래와 같다.
<form action="/api/images" method="post" enctype="multipart/form-data">
<label for="img"></label>
<input type="file" id="img" name="img", accept="image/*">
<input type="submit" value="제출">
</form>
form tag 내부에 accept="image/*"를 추가해 줌으로서, 이미지 형식 파일만 선택하게 만들 수 있다. 더 자세한 내용은 공식 문서를.... 👇
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
express 앱을 세팅할 차례.
위 form tag는 input type이 submit이고, action과 method가 정의되어있으므로, 제출 버튼을 누르면 바로 Http request가 만들어진다. 해당 uri에 응답할 api를 만들어준다.
apiRouter
.route("/images")
.post(uploadFiles.single("img"), (req, res) => {
const { file } = req;
console.log(file);
return res.send({ result: "success" });
});
공식 문서를 보면, multer를 미들웨어로 사용한다. 해당 미들웨어에서는, multer 모듈의 기본 설정을 해주는 듯 하다. 나는 파일이 저장될 경로만 지정해주었다.
import multer from "multer";
export const uploadFiles = multer({
dest: "uploads/",
});
나는 단일 이미지 파일을 업로드 할 것이므로, .single()을 사용했다. ()에 들어갈 문자열은 form 태그 내 input 태그의 name 속성과 같아야 한다.
실제 미들웨어를 사용할 때에는 아래 이미지를 보고 어떤 메서드를 사용할 지 선택해야 한다.
api controller에서 파일이 어떻게 생겼는지 확인했다. multer를 사용하면 파일은 req.file에 담겨 온다. array를 사용했을 경우 req.files로 파일이 온다.
딱 봐도 filename은 unique해 보인다. 프로젝트에 적용 시에는 path의 값을 db에 저장하면 될 듯 하다.
프로젝트 디렉터리에 uploads 폴더가 생기고 그 안에 파일이 저장된 것을 확인 할 수 있다.
express.static()을 사용해 정적 파일을 제공하는 것은 이미 여러번 시도해봤으므로, 다른 방법으로 파일을 제공해보고 싶었다. express Request 메서드 중 sendFile()있길래, 활용해 보았다.
페이지를 로딩할때 API를 호출한 뒤, 특정 이미지를 보내도록 만들었다.
apiRouter
.route("/images")
.post(uploadFiles.single("img"), (req, res) => {
const { file } = req;
console.log(file);
return res.send({ result: "success" });
})
.get(async (req, res) => {
console.log(process.cwd());
return res.sendFile(
process.cwd() + "/uploads/fef621f1c1e0e5394d7f4dea70c8f452"
);
});
res.sendFile("파일 경로");
로 사용한다. 경로는 절대경로로 만들어주었다.
<script>
const getOneImage = async () => {
const container = document.getElementById("img-container")
const response = await fetch ("/api/images",{
method : "GET"
});
console.log(response);
const blobImg = await response.blob();
console.log(blobImg);
const imgUrl = URL.createObjectURL(blobImg);
console.log(imgUrl);
const html = `<img src="${imgUrl}" alt="">`;
container.innerHTML = html;
}
window.onload = () => {
getOneImage();
};
</script>
페이지가 로드되면 GET /api/images/ 를 호출한다. 처음에는 response를 아무리 뒤져서 데이터가 나오지 않았다. 여기저기 수소문한 끝에 이미지, 동영상 등의 데이터는 blob 메서드를 한번 거쳐줘야 한다는 것을 알았다.
이 blob 객체를 어떻게 쓰지? 고민하다, blob을 url로 변환한 뒤 img src로 사용할 수 있다는 것을 알았다.
blob을 url로 변환한 결과는 아래와 같다.
성공........
클라이언트 api 호출이 잦아지므로, 서버 부하가 증가할 수 있다.또한 결정적으로, res.sendFile()은 1개의 파일만 보낼 수 있다.
결국 백엔드 서버 측에서 정적 파일을 제공한 뒤에, 클라이언트에서 직접 접근을 하도록 하는 방법밖에 없을 듯 하다.