미들웨어
앞의 글에서 Router() 를 이용해 모듈 형태로 생성된 라우터를 app.use 로 등록하여 사용했습니다. app.use 가 미들웨어를 등록하는 부분입니다.
또한, app.use(bodyParse()) 를 이용해 클라이언트가 body 에 포함한 데이터를 사용 가능하도록 파싱합니다. app.use 로 등록한 미들웨어는 모든 요청에 대해 동작하게 됩니다.
하지만 사용자 요청을 처리하기 위해 사용한 app.get, app.post, app.put, app.delete 등의 미들웨어는 요청 URL 과 method 에 따라 동작하는 미들웨어입니다.
우리가 사용자 요청을 처리하기 위해 라우팅을 한 부분 모두가 미들웨어를 등록한 과정이라고 할 수 있습니다.
사용자 요청을 라우팅하기 위해 콜백함수를 등록하는데, app.use 에 등록하는 미들웨어 또한 같은 형태로 콜백함수를 받습니다. 첫 번째 인자는 요청 객체, 두 번째 인자는 응답 객체, 세 번째 인자는 next 입니다.
미들웨어에서는 next 가 매우 중요합니다.
미들웨어 등록하기
app.use 를 이용해 모든 요청에 대해 처리하는 미들웨어를 등록할 수 있습니다.
const express = require('express');
let app = express();
const http = require('http');
app.use((req, res, next) => { // 미들웨어 등록
console.log('첫 째 미들웨어';
next()
})
app.get('/', (req, res, next) => { // 미들웨어 등록
res.send('hello world');
})
http.createServer(app).listen(3000, () => {
console.log('server on : 3000 PORT');
})
localhost:3000 으로 접속했을 시 결과
위 코드를 조금 더 자세히 살펴보겠습니다.
첫 번째 미들웨어
app.use((req, res, next) => { // 미들웨어 등록
console.log('첫 째 미들웨어';
next()
})
- 위 미들웨어는 app.use 로 등록했기 때문에 모든 요청에 대해 실행됩니다.
- console.log() 를 실행 후 다음 미들웨어를 호출합니다. 만약 next() 가 없다면 서버 측에만 첫 번째 미들웨어가 출력되고 클라이언트는 아무런 응답을 받지 못하게 됩니다.
- 만약 해당 미들웨어가 실행 중 문제가 발생하여 다음 미들웨어를 실행하지 못한다면 해당 부분에서 두 번째 인자인 응답 객체를 이용하여 클라이언트에게 응답을 해주어야 합니다.
- 미들웨어에서는 반드시 next() 를 호출해 다음 미들웨어를 호출해야하며, 만약 문제가 발생했다면 클라이언트에게 문제가 발생했다고 알려야 합니다.
두 번째 미들웨어
app.get('/', (req, res, next) => { // 미들웨어 등록
res.send('hello world');
})
- 이 부분은 모든 요청에 따라 실행되지 않고 요청된 method 와 URL 에 따라 실행되는 부분입니다.
- 여기에서는 굳이 next() 를 이용해 다음 미들웨어를 호출할 필요가 없습니다. 원하는 최종 로직의 코드를 다 실행했기 때문입니다.
함수 형태로 분리한 미들웨어
미들웨어도 복잡한 로직을 가질 수 있기 때문에 이럴 때는 함수 형태로 분리해 등록할 수 있습니다. 앞의 글에서 bodyParse() 를 등록했던 것처럼 함수 형태로 등록 가능합니다.
const express = require('express');
let app = express();
const http = require('http');
let firstMiddleware = (req, res, next) => {
console.log('첫 번째 미들웨어');
next();
}
app.use(firstMiddleware);
app.get('/', (req, res, next) => {
res.send('hello world');
})
http.createServer(app).listen(3000, () => {
console.log('server on : 3000 PORT');
})
- 위 코드는 처음 미들웨어를 등록했을 때와 동일하게 작동하는 코드입니다.
라우터에서 미들웨어 등록하기
라우터에서 미들웨어를 추가로 만들 수 있습니다.
다수의 API 가 있을 때 특정 API 만 실행하고 싶은 미들웨어가 있다면 다음과 같은 방법으로 미들웨어를 추가하면 됩니다. next() 가 다음 미들웨어를 호출한다는 개념이 중요합니다.
const express = require('express');
let app = express();
const http = require('http');
let firstMiddleware = (req, res, next) => {
console.log('첫 번째 미들웨어');
next();
}
app.get('/', firstMiddleware, (req, res, next) => {
res.send('hello world111');
});
app.get('/user', (req, res, next) => {
res.send('hello world222');
})
http.createServer(app).listen(3000, () => {
console.log('server on : 3000 PORT');
})
- 2 개의 API 가 있으며 '/' GET 요청에만 미들웨어가 추가되었습니다.
app.get('/', 미들웨어, 미들웨어...)
형태로 계속 등록이 가능하며 post, put, delete, use 모두 사용 가능합니다.- 단, next 를 통해서 다음 미들웨어를 제대로 호출해야 합니다.
미들웨어를 활용한 에러 처리
에러를 처리하는 미들웨어는 약간 특수한 형태로 이루어져 있습니다. 지금까지 미들웨어는 첫 번째 인자로 요청 객체, 두 번째 인자로 응답 객체, 세 번째 인자로는 next 객체를 받았습니다.
하지만 에러를 처리하는 미들웨어는 첫 번째 에러 객체를 받습니다. 그리고 두 번째 객체부터 네 번째 객체까지 요청, 응답, next 순서로 객체를 받게 됩니다.
app.use((err, req, res, next) => {
console.log(err);
})
에러 처리를 위한 미들웨어는 단순히 next 를 호출하면 됩니다.
이때, next() 를 실행하면서 인자를 넘겨 주어야합니다.
미들웨어를 이용한 에러 처리
const express = require('express');
let app = express();
const http = require('http');
app.get('/:id', (req, res, next) => {
let id = req.params.id;
if (id == 1) {
next('error 발생');
} else {
res.send('hello world');
}
});
app.use((err, req, res, next) => {
console.log(err);
res.status(500).send(err)
})
http.createServer(app).listen(3000, () => {
console.log('server on: 3000 PORT')
});
- id 값이 1 일 때 next('error occured') 가 실행되면 app.use((err,req,res,next)) 를 실행합니다.
- 에러 미들웨어의 첫 번째 인자인 에러 객체는 next() 로 전달한 첫 번째 인자를 받게 됩니다.
404 처리하기
가장 마지막에 있는 에러 처리 미들웨어 바로 위에 미들웨어를 추가하면 404 에러를 간단하게 처리할 수 있습니다.
// 생략
app.use((req, res, next) => { // 404 처리 미들웨어
console.log('404');
res.status(404).send('<h1>not found page</h1>');
});
app.use((err, req, res, next) => {
console.log(err);
res.status(500).send(err)
})
// 생략
- 등록한 라우터에서 일치하는 method 와 URL 이 있었다면 next 가 없기 때문에 404를 처리하는 미들웨어가 실행되지 않습니다.
- 하지만 일치하는 라우터가 없기 때문에 404를 처리하는 미들웨어가 실행됩니다.
정리
미들웨어에서 중요한 부분은 등록한 순서로 미들웨어를 실행한다는 것입니다.
대신, 다음 미들웨어를 실행하기 위해서는 next() 를 호출해야합니다. 그리고 에러를 처리하는 미들웨어는 특수하게 4개의 인자를 받으며 해당 미들웨어를 실행하기 위해서는 next() 에 첫 번째 인자를 넣어서 호출하면 됩니다.
express 에서 제공하는 미들웨어
모든 미들웨어를 직접 만들어 사용하진 않습니다.
대부분의 미들웨어를 express 에서 제공하기 때문에 npm install -s [모듈명]
으로 설치한 다음 사용하면 됩니다.
morgan
morgan 은 클라이언트의 요청 로그를 확인하는 미들웨어입니다.
200은 녹색, 404는 황색, 500 은 적색으로 표시되어 로그를 한 눈에 보기 편리합니다.
const logger = require('morgan');
app.use(logger('dev'));
body-parser
클라이언트가 body 에 포함한 데이터를 파싱하기 위해 필요한 미들웨어입니다.
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());
bodyParser 를 제대로 사용하기 위해서는 bodyParser.json() 의 형태로 호출해야합니다.
body 에는 데이터의 제한이 없지만 node.js 에서는 100Kb 로 제한이 걸려있습니다. 하지만 json() 에 옵션을 추가해 제한을 변경할 수 있습니다.
app.use(bodyParser.json({limit:5000000})); // 5Mb 까지 제한 해제
cookie-parser
헤더에 포함한 쿠키를 JSON 형태로 파싱하는 미들웨어입니다.
const cookieParser = require('cookie-parser');
app.use(cookieParser());
multer
multer 는 파일 업로드를 위한 미들웨어입니다.
express.static
정적파일 제공을 위한 미들웨어입니다.
express 는 CSS, JS, images 파일같은 정적 파일을 하나의 디렉토리에서 관리합니다.
passport
사용자 인증을 위한 미들웨어로 passport 를 이용하면 페이스북, 네이버에서 제공하는 인증시스템을 이용할 수 있으며 인증시스템을 직접 만드는 것도 가능합니다.
미들웨어 (라우터) 에서 주의할 점
하나의 미들웨어 (라우터) 에서 응답이 여러 번 등장하면 안됩니다.
만약
app.get('/', (req, res, next) =>{
//next() => 응답과 같이 실행되면 에러 발생
res.send('hello world1');
res.send('hello world2');
})
이런 형태로 만들어진 API 를 생성한다면 다음과 같은 에러가 발생하게 됩니다.
Error: Can't set headers after they are sent'
응답뿐만 아니라 next 와 응답을 같이 사용하는 경우도 마찬가지로 똑같은 에러가 발생하게 됩니다.
이 경우에는 조건분기를 이용해 next 와 응답하는 부분을 함께 사용할 수 있습니다.
app.get('/', (req, res, next) =>{
if(조건){
next();
}else{
res.send('hello world');
}
})
참고도서 : 자바스크립트로 서버와 클라이언트 구축하기
'Study > Express(node.js)' 카테고리의 다른 글
node.js ) 비동기 (asynchronous) 패턴 (0) | 2022.02.05 |
---|---|
express ) express-generator 프로젝트 생성하기 (0) | 2022.02.05 |
express ) Router (0) | 2022.01.17 |
node.js ) package.json 파헤치기 (0) | 2022.01.16 |
node.js ) 웹서버 기초 (2) - GET, POST, 정적/동적 파일제공 (0) | 2022.01.16 |
댓글