multer
이미지, 동영상 등을 비롯한 여러 가지 파일들을 멀티퍼트 형식으로 업로드할 때 사용하는 미들웨어입니다. 멀티퍼트 형식이란 다음과 같이 enctype이 multipart/form-data인 폼을 통해 업로드하는 데이터의 형식을 의미합니다.
다음과 같은 multipart.html이 있다면 멀티퍼트 형식으로 데이터를 업로드할 수 있습니다. 아직 서버 쪽 라우터를 만들지 않았으므로 예제가 실행되지는 않습니다.
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
멀티파트 형식으로 업로드하는 데이터는 개발자 도구 Network 탭에서 다음과 같이 보입니다. 이미지 하나를 선택하고 title 인풋에 제목이라고 적어서 업로드하면 다음과 같은 데이터가 전송됩니다.

이러한 폼을 통해 업로드하는 파일은 body-parser로는 처리할 수 없고 직접 파싱(해석)하기도 어려우므로 multer라는 미들웨어를 따라 사용하면 편리합니다. multer를 설치합니다.
$ npm i multer
multer 패키지 안에는 여러 종류의 미들웨어가 들어 있습니다. 미들웨어를 살펴보기 전에 기본적인 설정부터 알아봅시디.
const multer = require('multer');
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, 'uploads/');
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
done(null, path.basename(file.originalname, ext) + Date.now() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
multer 함수의 인수로 설정을 넣습니다. storage 속성에는 어디에(destination) 어떤 이름으로(filename) 저장할지를 넣었습니다. destination과 filename 함수의 req 매개변수에는 요청에 대한 정보가, file 객체에는 업로드한 파일에 대한 정보가 있습니다. done 매개변수는 함수입니다. 첫 번째 인수에는 에러가 있다면 에러를 넣고, 두 번째 인수에는 실제 경로나 파일 이름을 넣어주면 됩니다. req나 file의 데이터를 가공해서 done으로 넘기는 형식입니다.
현재 설정으로는 uploads라는 폴더에 [파일명+현재시간.확장자] 파일명으로 업로드하고 있습니다. 현재 시간을 넣어주는 이유는 업로드하는 파일명이 겹치는 것을 막기 위함입니다.
limits 속성에는 업로드에 대한 제한 사항을 설정할 수 있으며, 파일 사이즈(fileSize, 바이트 단위)는 5MB로 제한해두었습니다.
다만, 위 설정을 실제로 활용하려면 서버에 uploads 폴더가 꼭 존재해야 합니다. 없다면 직접 만들어주거나 다음과 같이 fs 모듈을 사용해서 서버를 시작할 때 생성합니다.
const fs = require('fs');
try {
fs.readdirSync('uploads');
} catch (error) {
console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
fs.mkdirSync('uploads');
}
설정이 끝나면 upload 변수가 생기는데, 여기에 다양한 종류의 미들웨어가 들어 있습니다.
먼저 파일을 하나만 업로드하는 경우(multipart.html과 같은 경우)에는 single 미들웨어를 사용합니다.
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file, req.body);
res.send('ok');
});
single 미들웨어를 라우터 미들웨어 앞에 넣어두면, multer 설정에 따라 파일 업로드 후 req.file 객체가 생성됩니다. 인수는 input 태그의 name이나 폼 데이터의 키와 일치하게 넣으면 됩니다. 업로드 성공 시 결과는 req.file 객체 안에 들어 있으며, req.body에는 파일이 아닌 데이터인 title이 들어 있습니다.
req.file 객체를 다음과 같이 생겼습니다.
{
fieldname: 'img',
originalname: 'nodejs.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'nodejs1514197844339.png',
path: 'uploads\nodejs1514197844339.png',
size: 53357
}
여러 파일을 업로드하는 경우 HTML의 input 태그에는 multiple을 쓰면 됩니다.
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="many" multiple />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
미들웨어는 single 대신 array로 교체합니다.
app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files, req.body);
res.send('ok');
});
업로드 결과도 req.file 대신 req.files 배열에 들어 있습니다.
파일을 여러 개 업로드하지만 input 태그나 폼 데이터의 키가 다른 경우에는 fields 미들웨어를 사용합니다.
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image1" />
<input type="file" name="image2" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
fields 미들웨어의 인수로 input 태그의 name을 각각 적습니다.
app.post('/upload',
upload.fields([{ name: 'image1' }, { name: 'image2' }]),
(req, res) => {
console.log(req.files, req.body);
res.send('ok');
},
);
업로드 결과도 req.files.image1, req.files.image2에 각각 들어 있습니다.
특수한 경우이지만, 파일을 업로드하지 않고도 멀티파트 형식으로 업로드하는 경우가 있습니다. 그럴 때는 none 미들웨어를 사용합니다.
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body);
res.send('ok');
});
파일을 업로드하지 않았으므로 req.body만 존재합니다.

실제로 multer 예제를 실습하고 싶다면 app.js를 다음과 같이 수정합니다. multipart.html도 되돌립니다.
...
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
const multer = require('multer');
const fs = require('fs');
try {
fs.readdirSync('uploads');
} catch (error) {
console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
fs.mkdirSync('uploads');
}
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, 'uploads/');
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
done(null, path.basename(file.originalname, ext) + Date.now() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
app.get('/upload', (req, res) => {
res.sendFile(path.join(__dirname, 'multipart.html'));
});
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file);
res.send('ok');
});
app.get('/', (req, res, next) => {
console.log('GET / 요청에서만 실행됩니다.');
...
multipart.html
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image1" />
<input type="file" name="image2" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
localhost:3000/upload에 접속해서 실습하면 됩니다.
'프로그래밍 언어 > NODE JS' 카테고리의 다른 글
| req, res 객체 살펴보기 (1) | 2025.07.06 |
|---|---|
| Router 객체로 라우팅 분리하기 (2) | 2025.07.03 |
| 미들웨어 특성 활용하기 (1) | 2025.06.27 |
| expression-session (0) | 2025.06.26 |
| cookie-parser (0) | 2025.06.22 |