Node

[Node] multer 모듈

효진인데요 2023. 8. 21. 23:32

 

0. multer란

 

  • 파일 업로드를 위해 사용되는 Node.js의 미들웨어
  • express로 서버 구축 시 가장 많이 사용되는 미들웨어
  • multipart/form-data 형식으로 단일 및 다중 파일 업로드를 지원하기 때문에 많이 사용됨

 

 

 

1. 설치 방법

 

 

Vscode의 터미널에서 npm i multer 명령어를 통해 설치할 수 있다. (일반적인 모듈 설치 방법과 동일)

 

 

 

 

이렇게 뜨며 설치가 완료되고 

 

 

package.json에 정상적으로 추가되는 것을 확인할 수 있다.

 

 

 

 

 


 

 

 

 

 

간단한 실습을 통해 어떻게 사용하는지 살펴보려 한다.

코드 안에 주석으로 자세한 설명을 작성해 두었으니 꼼꼼히 읽어보면 도움이 될 것 같다.

 

 

3. 간단 실습 - 파일 업로드

 

3.1 Single file upload

 

single()

  • 하나의 파일 업로드

 

 

app.js

 

const express = require('express');
const app = express();
const PORT = 8000;

// multer 관련 설정
const multer = require('multer');
const upload = multer({
  dest: 'uploads/', // dest: 클라이언트가 업로드한 파일ㅇ르 저장할 서버측 경로
});

app.set('view engine', 'ejs');
app.use('/views', express.static(__dirname + '/views'));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/', (req, res) => {
  res.render('index');
});

/*  
1. upload.single('')
- 하나의 파일을 업로드

upload.single('userfile')
- 클라이언트의 요청이 들어오면 multer 설정 (upload 변수)에 따라 파일을 업로드 한 후, 
  req.file 객체 생성'

single()인자 : input 태그의 name 속성과 일치시켜야 함
*/
app.post('/upload', upload.single('userfile'), (req, res) => {
  console.log(req.file);
  console.log(req.body);
  res.send('file upload success');
});

app.listen(PORT, () => {
  console.log(`${PORT} is opening!`);
});

 

 

 

index.ejs

 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>file upload</title>
  </head>
  <body>
    <h1>file upload</h1>
    <!-- 
    multer는 multipart가 아닌 폼에서는 동작하지 않음
    따라서 enctype="multipart/form-data" 속성 필수
    -->
    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="userfile" /> <br />
      <input type="text" name="title" /> <br /><br />
      <button type="submin">upload</button>
    </form>
  </body>
</html>

 

 

 

파일 선택 + title 입력

 

업로드 버튼 클릭 시

 

 

업로드 버튼 클릭 시 지정한 /upload 경로로 이동하여 성공 메시지를 찍어준다.

 

 

 

 

파일 업로드 후 코드 구조

 

파일을 업로드하면, dest로 설정해 둔 파일인 uploads가 자동적으로 생성되며, 업로드한 파일이 올라간다.

이 상태에서는 파일을 확인할 수 없으며, 뒤에 확장자를 붙이면 정상적으로 파일을 확인할 수 있다.

 

 

 

업로드가 되며 터미널에 req.file 객체가 뜬다.

이를 자세히 살펴보자.

 

 

 

fieldname 폼에 정의한 name 값
originalname  원본 파일명
encoding  파일 인코딩 타입
mimetype  파일 타입
destination  파일 저장 경로
filename  destination 저장된 파일명
path  업로드 된 파일 전체 경로
size  파일 크기

 

 

 

 

 

하지만 이렇게 확장자도 없이, 이름도 길고 정신없게(?) 저장되면 어떤 파일인지 확인하기 힘들고,

파일의 크기도 맘대로 저장되게 되면 파일이 많아졌을 경우 용량을 많이 잡아먹을 수 있게 된다.

 

 

 

이러한 경우, multer를 활용하여 세부 설정을 할 수 있다.

 

 

 


 

 

[ multer 세부 설정 ] 

 

 

// app.js

// multer 세부 설정
const uploadDetail = multer({
  // storage: 저장할 공간에 대한 정보
  storage: multer.diskStorage({
    destination(req, file, done) {
      // done: callback 함수
      // done(null, XXX) : 여기에서 null은 error을 의미하는 매개변수
      // 에러가  없으므로 "null"이라고 전달하여 콜백 함수를 호출
      done(null, 'uploads/'); // 파일을 업로드할 경로 설정
    },
    filename(req, file, done) {
      // extname: 원본 파일에서 확장자만 추출
      // 내장 모듈에서 업로드 될 파일의 "확장자"를 추출하여 ext 변수에 저장
      const ext = path.extname(file.originalname);
      // basename : 원본 파일에서 확장자를 제외한 파일 이름만 추출
      done(null, path.basename(file.originalname, ext) + Date.now() + ext);
    },
  }),
  // limits: 파일 제한 정보
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB , 파일 사이즈 제한
});



app.post('/upload', uploadDetail.single('userfile'), (req, res) => {
  console.log(req.file);
  console.log('----------');
  console.log(req.body);
  res.send('file upload success');
});

 

 

 

위 세부설정을 보면 filename 내부에 path 모듈을 많이 사용하고 있는 것을 확인할 수 있다.

 

path의 내장 모듈을 간단하게 살펴보자면 아래와 같다.

 

 

 

[ path 내장 모듈 ]

 

// path.js

// path 내장 모듈
const path = require('path');
const ext = path.extname('hello.txt'); // 파일명에서 확장자만 추출

// basename: 원본 파일에서 확장자를 제외한 파일 이름만 추출
const base = path.basename('hello.txt', ext); 

const result = base + Date.now() + ext; // 파일 이름 + 날짜 + 확장자

console.log(ext); // .txt
console.log(base); // hello
console.log(result);

/*
 파일명 결과에 왜 Date.now()를 추가하여 저장할까?
1.  파일 이름 중복을 막기 위해
2. 파일 이름만 보고 파일이 저장된 시점 유추 가능
*/

 

  • path.extname : 파일명에서 확장자만 추출
  • path.basename : 원본 파일에서 확장자를 제외한 파일 이름만 추출

 

 

 


 

 

 

 

 

동일한 파일을 올렸는데 multer 세부 설정으로 인해 파일 이름 등이 다르게 저장되는 것을 볼 수 있다.

 

 

 

 


3.2 Multi file upload 1

 

array()

  • 여러 파일을 업로드할 때 사용
  • 하나의 요청 안에 여러 개의 파일이 존재할 때

 

 

// app.js

/*
2. array() : 여러 파일을 한 번에 업로드

- uploadDetail.array('userfiles') : 
클라이언트 요청이 들어오면
multer tjfwjd (uploadDetail 변수)에 따라 파일을 업로드한 후, req,files 객체 생성
*/
app.post('/upload/array', uploadDetail.array('userfiles'), (req, res) => {
  console.log(req.files); // [ {파일 1_정보}, {파일 2_ 정보}, ...]   이렇게 배열 형식으로 찍힘
  console.log('----------');
  console.log(req.body);
  res.send('multifile upload success');
});

 

<!--index.ejs-->

<h2>Multi file upload ver.1</h2>
    <p>하나의 인풋에 여러 개의 파일을 업로드</p>
    <form action="/upload/array" method="post" enctype="multipart/form-data">
      <!-- 하나의 인풋에 여러 파일을 입력받고 싶다면 multiple 속성을 추가 -->
      <input type="file" name="userfiles" multiple /> <br />
      <input type="text" name="title" /> <br /><br />
      <button type="submit">upload</button>
    </form>

 

 

 

 

 

 

array를 사용하여 여러 파일을 하나의 인풋에 넣어 업로드하는 경우,

이렇게 배열 형식으로 여러 개의 파일에 대한 정보가 뜨는 것을 확인할 수 있다.

 

[ {파일 1_정보}, {파일 2_ 정보}, ...]   

이렇게 배열 형식으로 찍힌다.

 

 

 

3.3 Multi file upload 2

 

fields() 

  • 여러 파일을 업로드할 때 사용
  • 하나의 요청이 아닌 여러 개의 요청이 들어올 때

 

 

// app.js

/*
3. fields() : 여러 파일을 각각 인풋에 업로드

req.files에서 파일 정보를 확인
fields() 매개 변수로 input 태그의 name을 각각 넣기
*/
app.post(
  '/upload/fields',
  uploadDetail.fields([{ name: 'userfile1' }, { name: 'userfile2' }]),
  (req, res) => {
    console.log(req.files); 
    // {userfile1 : [ {파일정보} ], userfile2 : [ {파일정보} ]}  
    // 이렇게 객체 안에 배열 형식으로 각 파일 정보가 찍힘
    console.log('----------');
    console.log(req.body);
    res.send('multifile upload success');
  }
);

 

<!--index.ejs-->

<h2>Multi file upload ver.2</h2>
    <p>여러 인풋에 여러 개의 파일을 업로드</p>
    <form action="/upload/fields" method="post" enctype="multipart/form-data">
      <!-- 하나의 인풋에 여러 파일을 입력받고 싶다면 multiple 속성을 추가 -->
      <input type="file" name="userfile1" /> <br />
      <input type="text" name="title1" /> <br /><br />
      <input type="file" name="userfile2" /> <br />
      <input type="text" name="title2" /> <br /><br />
      <button type="submit">upload</button>
    </form>

 

 

 

 

 

 

 

fields()를 사용하여 여러 개의 파일을 각각 다른 인풋에서 넣어 업로드하는 경우에는

이렇게 req.files이 반환되는 형태가 조금 다른 것을 확인할 수 있다.

 

이런 경우, 

{ userfile1 : [ {파일정보} ], userfile2 : [ {파일정보} ] }  

이런 식으로 객체 안에 배열 형식으로 각 파일 정보가 찍힌다.

 

 

 

 

 

728x90