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>


업로드 버튼 클릭 시 지정한 /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 : [ {파일정보} ] }
이런 식으로 객체 안에 배열 형식으로 각 파일 정보가 찍힌다.
'Node' 카테고리의 다른 글
[Node] JWT 토큰 구현 (0) | 2023.11.03 |
---|---|
[새싹X코딩온] 1차 프로젝트 회고록 : 새싹인 (0) | 2023.10.06 |
[Node] nodemon 설치 및 사소한 오류 / nodemon: command not found (0) | 2023.09.09 |
[Node] Node.js 개념과 특징 (+런타임) (0) | 2023.08.10 |
[Node] NPM 기초 (0) | 2023.01.20 |