프로그래밍 언어/NODE JS

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