
1. 들어가며
- Multer 미들웨어를 이용해서 파일을 업로드하는 방법을 배워보기
2. Multer 를 이용한 파일 업로드 기능 구현
form 요소에서 post 방식으로 정보를 전송하면 요청의 body에 담겨서 전송된다.
예를 들어 아래와 같은 폼양식이 있을 때

위와같이 정보를 제출하면

이런식으로 담겨서 요청이 전송되고 받을 수 있다.
(참고로 post 전송방식에서 원래는 안보이는데 body-parser라는 미들웨어를 사용해서 위와 같이 보이는 것이다)
그러나 멀티미디어 파트의 데이터는 처리하지 못한다.

위와 같이 파일을 선택해서 업로드를 하면

이런식으로 파일 이름만 보여준다.
텍스트 데이터의 경우 그냥 텍스트만 추출을 해서 사용할 수 있기 때문에 문제가 없지만
멀티미디어 같은 경우 이름만 쓰는게 아니라 파일 자체를 쓰기 때문에 따로 처리를 해줘야한다.
그래서 Multer 미들웨어를 이용해서 멀티미디어 파트의 데이터를 처리한다.
multer 는 express 처럼 따로 npm i multer 커맨드로 설치해주고
모듈을 불러와야한다.
const express = require('express')
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
const app = express()
app.post('/upload', upload.single('image'), (req, res)=>{
console.log(req.file);
})
그리고 form 요소에 꼭!
enctype="multipart/form-data"
라고 명시해줘야한다.
<form action="/upload" method="post" enctype="multipart/form-data">
</form>
이런식으로 해서 아래 예시에서 제출을 누르고

node 창을 확인하면

파일에 대한 상세한 정보와 어디에 저장되어 있는지도 나온다.
그런데 여기서 이상한 것이 있다.
filename에서 확장자가 보이지 않는다.
그래서 직접 확인하러 가보면

이렇게 확장자가 없이 저장되서 이미지가 보이지 않는 것을 볼 수 있다.
물론 원래 이미지 확장자를 이름바꾸기를 통해서 적어주면 보이기는 하다

하지만 이미지가 업로드될때마다 확장자를 확인해서 일일히 이름을 수정해주는 작업은 너무 비효율적이다.
그래서 우리 multer는 업로드 설정을 세부적으로 할 수 있는 방법을 제공해준다.
const express = require('express')
const multer = require('multer')
const path = require('path')
const app = express()
// const upload = multer({dest : 'uploads/'})
// 업로드 설정
const uploadDetail = multer({
storage : multer.diskStorage({ // storage : 저장에 대한 설정
destination(req, file, done){ // 저장할 경로 설정
done(null, 'uploads/') // 파일을 저장할 경로
},
filename(req, file, done){ // 디스크에 저장될 파일명 설정
const ext = path.extname(file.originalname) // path.extname() -> 파일 확장자 추출
done(null, path.basename(file.originalname, ext) + ext) // 저장될 파일명 설정
}
})
})
app.post('/upload', uploadDetail.single('image'), (req, res)=>{
console.log(req.file);
})
위의 코드처럼 multer 객체 내에서 세부적인 설정을 해줄수가 있다.
저장할 경로는 물론 저장될 이름까지 설정을 해줄 수 있다.
다음 코드로 바꾸고 실행을 해보면

filename이 내가 설정한 대로 저장이 되었음을 확인할 수 있다.

디스크에서도 이렇게 확인을 해줄 수 있다.
아 그리고 위의 방법은 하나의 input에서 하나의 파일을 업로드 했을 때의 방법이다.
그래서 메서드를 자세히 살펴보면 single()이라고 되어있는 것을 확인할 수 있다.
single() 메서드 내의 인자값으로는 file 타입의 input요소의 name 속성값을 적어주면 된다.
app.post('/upload', uploadDetail.single('image'), (req, res)=>{
console.log(req.file);
})
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="name">이름</label>
<input type="text" name="name" id="name"><br>
<span>좋아하는 과일</span>
<select name="fruits" id="fruits">
<option value="사과">사과</option>
<option value="딸기">딸기</option>
<option value="포도">포도</option>
<option value="바나나">바나나</option>
</select>
<br>
<input type="file" name="image" id="image" >
<br>
<button>제출</button>
</form>
그런데 인터넷을 하다보면 하나의 파일뿐 아니라 여러개의 파일을 올릴 때도 있었을 것이다.
물론 이것을 제공해주는 메서드도 있다.
array() 메서드를 이용하면 하나의 인풋에 여러개의 파일을 올릴 수 있다.
그리고 file 타입의 input 요소에 multiple 속성을 적어줘야 한다.
그리고 이것은 복수의 파일을 업로드 하는 것이기 때문에
single 메서드와는 달리 req.file이 아닌
req.files 를 써서 요청을 받아야 한다.
app.post('/upload', uploadDetail.array('image'), (req, res)=>{
console.log(req.files);
})
<form action="/upload" method="post" enctype="multipart/form-data">
<label for="name">이름</label>
<input type="text" name="name" id="name"><br>
<span>좋아하는 과일</span>
<select name="fruits" id="fruits">
<option value="사과">사과</option>
<option value="딸기">딸기</option>
<option value="포도">포도</option>
<option value="바나나">바나나</option>
</select>
<br>
<input type="file" name="image" id="image" multiple>
<br>
<button>제출</button>
</form>
그러면 아래와 같이 파일을 여러개 선택할 수 있다.

제출을 한 후 node에서 확인을 해보면

이렇게 배열안에 여러개 담겨서 파일 정보가 저장된 것을 확인할 수 있다.
여러개의 인풋에서 여러개의 파일을 전달받는 방법도 있다.
fields() 메서드를 이용하면 된다.
그리고 이번에는 조금 더 응용을 해서 input의 text 타입에서 이름을 적어준대로 파일을 저장해보고자 한다.
그러기 위해서는 multer 객체내의 세부설정을 조금 바꿔주면 된다.
<form action="/uploads" method="post" enctype="multipart/form-data">
<input type="text" name="cookie1Name" id="cookie1Name">
<input type="file" name="cookie1" id="cookie1">
<br>
<input type="text" name="cookie2Name" id="cookie2Name">
<input type="file" name="cookie2" id="cookie2">
<br>
<input type="text" name="cookie3Name" id="cookie3Name">
<input type="file" name="cookie3" id="cookie3">
<br>
<button>제출</button>
</form>
먼저 form 요소는 위와 같이 정해주었다.
그럼 모양이 아래와 같이 나온다.

왼쪽의 텍스트 인풋에 파일 이름을 정하고 제출을 하면 각각 정해진 이름대로 저장되도록 설정할 것이다.
그를 위해 multer 객체내의 세부 설정을 아래와 같이 바꿔주었다.
const uploadDetail = multer({
storage : multer.diskStorage({
destination(req, file, done){
done(null, 'uploads/')
},
filename(req, file, done){
const ext = path.extname(file.originalname)
const inputName = file.fieldname + 'Name'
const imgName = req.body[inputName]
done(null, imgName + ext)
}
})
})
app.post('/uploads', uploadDetail.fields(
[{name : 'cookie1'}, {name : 'cookie2'}, {name : 'cookie3'}]),
(req, res)=>{
console.log(req.files);
})
multer 객체 내에서 req.body를 부르면 똑같이 body 객체내에 저장된 정보를 받을 수 있다.
그래서 req.body를 불러와서 input text의 name값을 받아오고
그 안의 정보를 받아오도록 설정하고 이름으로 정하도록 했다.
그리고 fields 메서드는 인자값을 배열형태로 전달해야 한다.
배열내의 객체 형식으로 전달하고 이또한 여러개의 파일이라 req.files 내에 저장된다.

위와 같이 이름을 정해주고 제출을 눌렀다.
그리고 node 창을 확인하면

객체형태로 파일이 저장되며 key값이 fields() 안에 전해준 name 값으로 지정된 것을 확인할 수 있다.
또한 이름이 멀터 객체 내에서 설정한대로 저장된것을 확인할 수 있다.

디스크에도 마찬가지로 설정한 이름대로 저장되었다.
아무튼 이런식으로 응용을 하면 일반적인 사이트에서 회원가입을 했을때처럼
화면을 구성할 수 있다.

이런식으로 말이다.
그리고 전에 배웠던 동적 fom 전송을 응용하면 화면이 바뀌지 않고
이미지를 업로드하고 보여주는 것을 구현할 수 있다(프사 바꿀때처럼 말이다)

이런 식으로 말이다. 나중에 시간나면 좀 더 기능을 추가하고 구현해서 포스팅 해봐야겠다.
3. 마치며
multer를 이용해서 파일업로드 기능까지 구현하니까 정말 웹개발을 하는 듯한 기분이 든다.
좀 더 세부적으로 연습을 해서 정말 웹사이트에서 프로필 사진이나 닉네임을 바꾸는 것처럼 기능을 구현하고 싶은 욕심이 생긴다.
4. Reference
1. 코딩온 강의 자료
2. https://github.com/expressjs/multer/blob/master/doc/README-ko.md
multer/doc/README-ko.md at master · expressjs/multer
Node.js middleware for handling `multipart/form-data`. - expressjs/multer
github.com
'Study > Node.js' 카테고리의 다른 글
| [새싹x코딩온] 웹 개발자 부트캠프 과정 7주차 회고 | Node.js + MySQL 연결(MVC 패턴 적용) (0) | 2024.06.27 |
|---|---|
| [새싹x코딩온] 웹 개발자 부트캠프 과정 7주차 회고 | MVC 패턴으로 프로젝트 만들기 (0) | 2024.06.25 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 6주차 회고 | 동적 form 전송 (0) | 2024.06.18 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 5주차 회고 | Express.js 로 웹 서버 만들기 (0) | 2024.06.13 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 5주차 회고 | node.js readFile()과 readFileSync()(비동기와 동기의 차이) (0) | 2024.06.11 |