안녕하세요. 이번 시간에는 multer를 사용해 이미지를 업로드 및 폼데이터를 처리하는 것을 알아보겠습니다.
보통 JSON 형식으로 된 데이터는 AJAX로든 폼 태그로든 쉽게 업로드할 수 있습니다. 하지만 이미지 파일만큼은 사람들의 골치를 썩이는데요. express와 함께 사용하면 쉽게 이미지 업로드를 도와주는 multer 모듈이 있습니다.
npm i multer
이제 설치된 multer를 사용해 코딩을 해봅시다. 익스프레스 라우터 부분에 다음을 추가해줍니다.
const multer = require('multer');
// 기타 express 코드
const upload = multer({ dest: 'uploads/', limits: { fileSize: 5 * 1024 * 1024 } });
app.post('/up', upload.single('img'), (req, res) => {
console.log(req.file);
});
이제 폼데이터나 폼 태그를 통해 업로드한 이미지를 올리면 req.file
로 정보가 들어오고, dest 속성에 지정해둔 경로에 이미지가 저장됩니다. limits 속성은 선택 사항인데 여러 가지 제한을 걸 수 있습니다. 위에서는 파일 사이즈를 5MB로 제한했습니다. 폼데이터로 업로드하는 강좌는 여기를 참고하세요. upload.single('img')
미들웨어를 라우터 콜백함수 전에 끼워넣었는데요. 폼데이터의 속성명이 img이거나 폼 태그 인풋의 name이 img인 파일 하나를 받겠다는 뜻입니다. 이미지가 아닌 나머지 데이터는 그대로 req.body
에 들어옵니다.
만약 이미지를 하나가 아닌 여러 개를 받고 싶다 하면 upload.array('키', 최대파일개수)
하면 됩니다. req.file
대신 req.files
에 정보가 담깁니다.
app.post('/up', upload.array('img'), (req, res) => {
console.log(req.files);
});
만약 여러 개의 키로 이미지를 올렸다면 upload.fields
를 써야 합니다(이외에 upload.none
도 있습니다). 사용한 키들을 배열 안에 넣어주면 됩니다.
app.post('/up', upload.fields([{ name: 'img' }, { name: 'photos' }]), (req, res) => {
console.log(req.files);
});
문제는 uploads 폴더에 뭔가 생성이 되긴 하는데 이름도 ce243370b74107493fea0743d249a176처럼 이상하게 바뀌어있고 확장자도 붙어 있지 않아 쓸 수가 없습니다. 사실 이것은 보안상 의도된 것이지만 지금은 불편하기 때문에 이름이 원래대로 나오게 해보겠습니다. dest 속성 대신 storage 속성을 사용해 upload 변수에 넣어주면 됩니다.
const upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
}),
});
네. 조금 복잡해졌긴 하지만 위의 코드를 보시면 저장될 경로(destination)과 파일명(filename)을 조작하고 있음을 알 수 있습니다. 이제 다시 파일을 올리면 원본 파일명 그대로 올라갑니다. 그런데 파일명이 중복되는 경우 문제가 생길 수 있죠. 파일명을 타임스탬프로 해서 중복되지 않게 해봅시다.
const path = require('path');
const upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, new Date().valueOf() + path.extname(file.originalname));
}
}),
});
위와 같이 하시면 타임스탬프.확장자 형식으로 파일명이 지정됩니다.
지금까지는 실제 디스크에 파일을 업로드했는데요. S3같은 곳에 업로드하시는 분들도 있을 겁니다. S3에 업로드하는 방식은 크게 두 가지가 있는데요. 디스크에 있는 파일을 업로드하거나, 파일 버퍼(메모리에 저장)를 업로드하는 겁니다. 파일을 S3에 업로드한 후에는 남아있는 파일을 지워줘야 하는데 이게 번거롭죠. 그래서 처음부터 메모리에 파일을 버퍼 형식으로 저장하고, 그것을 업로드하는 겁니다.
const upload = multer({
storage: multer.memoryStorage(),
});
메모리스토리지를 쓸 경우는 req.file
이나 req.files
안의 파일 데이터(객체)에, 디스크스토리지 전용 속성인 destination, filename, path 대신 buffer라는 속성이 새로 생기고 그 값으로 버퍼들이 저장됩니다. 이 버퍼를 사용해서 S3에 버퍼로 업로드하시면 됩니다. 이 방식의 단점은 파일이 여러 개고, 용량이 너무 크면 메모리를 초과해서 서버가 멈춰버릴 수도 있습니다. 보통은 그럴 일은 없겠지만 그럴 수도 있다는 것을 알아두세요. 이러한 문제가 걱정되고, 버퍼를 다루는 게 어렵다면 multer-s3 패키지를 고려해보세요.