-
ExpressJS - express.js 활용2(Multer, Router, req와 res객체)FrameWork/Express.js 2024. 3. 20. 08:54
express.js 파일 업로드 하기
multer
이미지, 동영상등을 비롯한 여러가지 파일들을 멀티파트 형식으로 업로드할 때 사용하는 미들웨어이다. 멀티파티 형식이란 enctype이 multipart/form-data인 폼을 통해 업로드하는 데이터의 형식을 의미한다.
form 태그의 enctype이 multipart/form-data인 경우
- body-parser로는 요청 본문을 해석할 수 없음
- multer 패키지 필요
<form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="image"/> <input type="text" name="title"/> <button type="submit">업로드</button> </form>
multer 함수를 호출
- storage는 저장할 공간에대한 정보
- diskStorage는 하드디스크에 업로드 파일을 저장한다는 것
- destination은 저장할 경로
- filename은 저장할 파일명(파일명+날짜+확장자 형식)
- Limits는 파일 개수나 파일 사이즈를 제한할 수 있다.
- 실제 서버 운영 시에는 서버 디스크 대신에 S3같은 스토리지 서비스에 저장한다.
- Storage 설정만 바꿔주면 된다.
multer 모듈 호출
const multer = require('multer');
multer 자체가 미들웨어라기 보다는 multer 함수를 호출하면 호출한 함수안에 4가지의 미들웨어가 들어있다.
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매개변수는 함수이다. done를 각각 destintion, filename 함수에서 사용할 때 에러가 발생한다면 1)첫 번째 인수에는 에러를 넣고, 2)두 번째 인수에는 실제 경로 나 파일 이름을 넣어주면 된다. req나 file의 데이터를 가공해서 done으로 넘기는 형식이다.
현재 설정으로는 uploads라는 폴더에 [파일명+현재시간.확장자] 파일명으로 업로드하고있다. 현재 시간을 넣어주는 이유는 업로드하는 파일명이 겹치는 것을 막기 위해서다.
limits 속성에는 업로드에 대한 제한 사항을 설정할 수 있다. 파일사이즈(fileSize,바이트 단위)는 5MB로 제한해두었다.
uploads 폴더
다만 위 설정을 실제로 활용하기 위해서 서버에 uploads 폴더가 꼭 존재해야 한다. 없다면 직접 만들어 주거나 위 다음 코드 와 같이 서버를 시작할 때 생성한다.
try{ fs.readdirSync('uploads'); } catch (error){ console.log('uploads 폴더가 없어 uploads 폴더를 생성합니다.'); fs.mkdirSync('uploads'); }
위 코드는 서버가 시작되기 전에 작동하는 것이므로 sync 키워드가 붙은 함수를 사용해도된다. ⛔이 외에 서버 코드에서 sync 키워드가 붙은 함수를 사용하는 것은 주의한다.
라우터와 upload 미들웨어 연결
app.post('/upload',upload.single('image'),(req,res)=>{ console.log(req.file,req.body); res.send('ok'); });
multer() 메서드를 안에 옵션설정을 끝내고 이 결과를 upload 변수에 담는다. 이 upload 변수에 다양한 종류에 미들웨어가 들어 있다. 파일을 하나만 업로드하는 경우에는 multer.single() 미들웨어를 사용한다. 또 해당 미들웨어 함수에 들어가는 파라미터 'image'는 input 태그의 name 속성의 값이다.
이미지를 하나만 upload할 경우는 upload.single('image')미들웨어에서 이미지 업로드를 처리하고 req.file에 업드에 관한 정보를 넣어준다.
이미지 업로드는 모든 라우터에서 일어나는게 아니라 특정 라우터에서만 발생하는 event이기때문에 app.use()를 사용해서 등록하지 않는다.
이미지가 정상적으로 upload되면 위와 같은 req.file 정보를 반환한다.
e.preventDefault()
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> document.getElementById('form').getEventLisner('submit',(e) => { //해당 이벤트가 기본적으로 수행하는 동작이 실행되지 않는다. e.preventDefault(); const formData = new FormData(); formData.append('image',e.target.image.files[0]); formData.append('title',e.target.title.value) axios.post('/upload',formData); }); </script>
javascript를 이용하면 FormData를 통해서 multipart/form-data를 보낼 수 있는데, javascript를 이용할 때는 위 코드와 같이 e매개변수를 통해 image와 title의 value를 추출 할 수 있다. name이 title인 input태그의 type=text로 일반 string이기 때문에 이경우 req.file에 배열로 들어가는 것이 아니라 req.body.titile로 req.body에 들어간다.즉 multer가 req.file과 req.body를 모두 파싱해주는 것이다.
input file이 하나인 경우 multiple
<form id="form" action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="image" multiple /> <input type="text" name="title"> <button type="submit">업로드</button> </form> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> document.getElementById('form').getEventLisner('submit',(e) => { //해당 이벤트가 기본적으로 수행하는 동작이 실행되지 않는다. e.preventDefault(); const formData = new FormData(); formData.append('image',e.target.image.files[0]); formData.append('image',e.target.image.files[1]); formData.append('image',e.target.image.files[2]); formData.append('title',e.target.title.value) axios.post('/upload',formData); }); </script>
file 타입의 input 태그에 multiple 속성을 추가하면 이미지를 한장이 아닌 여러장 업로드할 수 있다. 그리고 script 코드에서는 formData.append('image',~~)에 이미지 여러개를 넣을수 있다. formData.append('image',~~)의 type이 배열일 되는 셈이다.
multiple일때 서버단 코드
app.post('/upload/multy',upload.array('image'),(req,res)=>{ console.log(req.files); res.send('ok'); });
여러장을 업로드하는경우에는 서버단에서도 upload.array('image') 미들웨어를 사용해야 여러장의 image가 upload 된다.
input file이 여러 개인 경우 multiple
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>multer</h1> <form id="form" action="/upload/fields" method="post" enctype="multipart/form-data"> <input type="file" name="image1" /> <input type="file" name="image2" /> <input type="file" name="image3" /> <input type="text" name="title"> <button type="submit">업로드</button> </form> </body> </html>
app.post('/upload/field',upload.fields([{name:'image1'},{name:'image2'},{name:'image3'}]), (req,res)=>{ console.log(req.files); res.send('ok'); });
file 타입의 input 태그 자체가 여러개일 경우에는 서버단에서 upload.array()를 사용하는 것이 아니라, upload.fields() 미들웨어를 사용하고 파라미터로 위코드와 같이 key가 name인 객체를 배열 형태로 넣어준다.
결과도 각 객체의 name의 key가 되어 반환되는 것을 확인 할 수 있다.
upload.none()
<body> <h1>multer</h1> <form id="form" action="/upload/fields" method="post" enctype="multipart/form-data"> <input type="text" name="title"> <button type="submit">업로드</button> </form> </body>
app.post('/upload',upload.none(),(req,res) =>{ res.send('ok'); });
위 HTML 파일처럼 form으로 전송된 이미지가 없는데 form의 enctype이 multipart/form-data일때, 서버단에서는 이미지를 업로드 하지 않지만, multer를 이용해서 다른 타입의 fromData를 파싱할 때 사용한다.
Router
app.js가 길어지는 것을 막을 수 있다.
routes/index.js
const {Router} = require('express'); const multer = require('multer'); const router = Router(); router.get('', (req,res) => { // res.sendFile(path.join(__dirname,'index.html')); res.send('Hello Express'); }); module.exports = router;
routes/user.js
const { Router } = require('express'); const router = Router(); router.get('',(req,res) =>{ res.send('Hello User'); }) module.exports = router;
app.js에서 app.get()같은 메서드가 라우터 부분이다. 라우터를 많이 연결하면 app.js 코드가 매우 길어지므로 익스프레스에서는 라우터를 분리할 수 있는 방법을 제공한다. routes 폴더를 만들고 그 안에 index.js와 user.js를 작성한다.
app.js
const path = require("path"); const indexRouter = require('./routes'); const userRouter = require('./routes/user'); // 라우터 등록 app.use('/',indexRouter); app.use('/user',userRouter);
indexRouter를 ./routes로 reqire할 수 있는 이유는 index.js는 생략할 수 있기 때문이다. require('./routes/index.js')와 require('./routes')는 같다.
index.js와 user.js는 모양과 구조가 거의 비슷하지만, 다른 주소의 라우터 역할을 하고 있다. app.use로 연결할 때의 차이 때문이다.
- indexRouter는 app.use('/')에 연결
- userRouter는 app.use('/user')에 연결
app.user로 라우터를 연결할 때는 주소가 합쳐진다는 것을 염두해 두자. 서버를 실행 한뒤에 localhost:3000과 localhost:3000/user로 접속하면 각각에 해당하는 응답을 받을 수 있다.
URL의 param과 query
요청 주소
- http://localhost/user/123?limit=3&skip=10
router.get('/:id',(req,res) =>{ console.log('params = ',req.params); console.log('query = ', req.query); res.send('ok'); })
위와 같은 코드를 요청 주소로 호출하면 콘솔에서 params와 query의 출력 결과를 확인할 수 있다.
라우터 그룹화 하기
- 주소는 같지만 HTTP 메서드가 다른 코드가 존재할 때
router.get('abc',(req,res)=>{ res.send('GET /abc'); }); router.post('abc'(req,res)=>{ res.post('POST /abc') });
- router.route로 묶음
router.route('/abc') .get((req, res)=>{ res.send('GET /abc'); }) .post((req,res) => { res.send('POST /abc'); }).
req, res 객체 살펴보기
익스프레스의 req, res 객체는 http 모듈의 req,res 객체를 확장한 것이다. 기존 http 모듈의 메서드도 사용할 수 있고, 익스프레스가 추가한 메서드나 속성을 사용할 수도 있다. 예를 들어 res.writeHead, res.write, res.end 메서드를 그대로 사용할수 있으면서 res.send나 res.sendFile같은 메서드도 사용할 수 있다. 다만, 익스프레스의 메서드가 워낙 편리하기 때문에 기존의 http 모듈의 메서드는 잘 사용되지 않는다.
req
- req.app : 객체를 통해 app 객체에 접근할 수 있다. req.app.get('port')와 같은 식으로 사용할 수 있다.
- req.body : body-parser 미들웨어가 만드는 요청의 본문을 해것한 객체이다.
- req.cookies : cookies-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체이다.
- req.ip: 요청의 ip 주소가 담겨 있다. 다만, 프록시 서버가 중간에 껴있는 등 정확히 클라이언트의 ip를 받아오는 것이 아닐 수도 있다는 점에 주의하자.
- req.params : 라우트 매개변수에 대한 정보가 담긴 객체이다.
- req.query : 쿼리스트링에 대한 정보가 담긴 객체이다.
- req.singedCookies : 서명된 쿠키들은 req.cookies 대신 여기에 담겨있다.
- req.get(헤더이름) : 헤더의 값을 가져오고 싶을 대 사용하는 메서드이다.
res
- res.app : req.app 처럼 res 객체를 통해서 app 객체에 접근할 수 있다.
- res.cookie(키, 값, 옵션) : 쿠키를 설정하는 메서드이다.
- res.clearCookie(키, 값, 옵션) : 쿠키를 제거하는 메서드이다.
- res.end() : 데이터 없이 응답을 보낸다.
- res.json(JSON) : JSON 형식의 응답을 보낸다.
- res.redirect(주소) : 리다이렉트할 주소와 함께 응답을 보낸다.
- res.render(뷰, 데이터) : 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드이다.
- res.send(데이터) : 데이터와 함께응답을 보낸다. 데이터는 문자일 수도 있고, HTML일 수도 있으며, 버퍼일 수도 있고 객체나 배열일 수도 있다.
- res.sendFile(경로) : 경로에 위치한 파일을 응답한다.
- res.set(헤더,값): 응답의 헤더를 설정한다.
- res.status(코드) : 응답 시의 HTTP 상태 코드를 지정한다.
체이닝
res .status(201) .cookie('test','test') .redirect('/admin');
req나 res 객체의 메서드는 위 코드와 같이 메서드 체이닝을 지원하는 경우가 많다. 메서드 체이닝을 활용하면 코드양을 줄일 수 있다.
[출처 - [개정3판]Node.js 교과서 - 기본부터 프로젝트 실습까지, 저 조현영]
https://www.inflearn.com/course/%EB%85%B8%EB%93%9C-js-%EA%B5%90%EA%B3%BC%EC%84%9C
'FrameWork > Express.js' 카테고리의 다른 글
ExpressJS - express.js 활용1(next활용법, 자주 쓰는 미들웨어) (0) 2024.03.19 ExpressJS - express.js 서버 시작하기(setting, 미들웨어) (0) 2024.03.18 express - Middleware(미들웨어)1 (0) 2023.02.15 Express 미들웨어에 중요한 특징 (0) 2022.08.04 Express 서버 만들기 Application (0) 2022.08.03