app.use( (req, res, next) => {
console.log('모든 요청에 다 실행된다.');
next();
});
미들웨어는 req, res, next를 매개변수 가지는 함수(에러 처리 미들웨어만 예외적으로 err, req, res, next를 가짐)로서 app.use나 app.get, app.post 등을 사용한다. 특정한 주소의 요청에만 미들웨어가 실행되게 하려면 첫 번째 인수로 주소를 넣으면 된다.
app.use(
morgan('dev'),
express.static('/', path.join(__dirname, 'public')),
express.json(),
express.urlencoded({ extended: false }),
cookieParser(process.env.COOKIE_SECRET)
);
위와 같은 방법으로 동시에 여러 개의 미들웨어를 적용할 수도 있다.
각 미들웨어는 다음 미들웨어로 넘어가기 위해서 next 함수를 호출해야 한다.
위 소스의 경우 미들웨어들이 내부적으로 next를 호출하고 있으므로 연달아 쓸 수 있다.
next를 호출하지 않는 미들웨어는 res.send나 res.sendFile 등의 메서드로 응답을 보내야 한다.
express.static
과 같은 미들웨어는 정적 파일을 제공할 때 next 대신 res.sendFile 메서드로 응답을 보낸다. 따라서 정적 파일을 제공하는 경우 express.json, express.urlencoded, cookieParser 미들웨어는 실행되지 않는다.
미들웨어 작성 순서에 따라 어떤 미들웨어는 실행되지 않을 수도 있으니 주의해야한다.
만약 next도 호출하지 않고 응답도 보내지 않으면 클라이언트는 응답을 받지 못해 계속 기다리게 된다.
next에 인수를 넣어서 특수한 동작을 실행시킬 수 있다. route 라는 문자열을 넣으면 다음 라우터의 미들웨어로 바로 이동하고, 그 외의 인수를 넣는다면 바로 에러 처리 미들웨어로 이동한다. 이때의 인수는 에러 처리 미들웨어의 err 매개변수가 된다. 라우터에서 에러가 발생할 때 에러를 next(err)을 통해 에러 처리 미들웨어로 넘긴다.
<aside>
💡 next(err)
→ (err, req, res, next) => {}
</aside>
미들웨어 간에 데이터를 전달하는 방법도 있다. 세션을 사용한다면 req.session 객체에 데이터를 넣어도 되지만, 세션이 유지되는 동안에 데이터도 계속 유지된다는 단점이 있다. 만약 요청이 끝날 때까지만 데이터를 유지하고 싶다면 req 객체에 데이터를 넣어두면 된다.
app.use((req, res, next) => {
req.data = '데이터 넣기';
next();
}, (req, res, next) => {
console.log(req.data); //데이터 받기
next();
});
현재 요청이 처리되는 동안 req.data를 통해 미들웨어 간에 데이터를 공유할 수 있다. 새로운 요청이 오면 req.data는 초기화 된다. (예로 req.data로 작성한거고 다른걸로 작성해도 된다. 다만 다른 미들웨어와 겹치지 않게 조심해야한다.)
app.set과 req.data의 차이?
app.set
으로 익스프레스에서 데이터를 저장할 수 있지만 익스프레스에서 전역적으로 사용되므로 사용자 개개인의 값을 넣기에는 부적절하다. 웹 전체의 설정을 공유할 때 사용하는게 좋다. req 객체는 요청을 보낸 사용자가 개개인에게 귀속되므로 req 객체를 통해 개인의 데이터를 전달하는 것이 좋다.
미들웨어를 사용할 때 유용한 패턴 한가지?
app.use(morgan('dev'));
또는
app.use((req, res, next) => {
morgan('dev')(req, res, next);
});
이 패턴이 유용한 이유는 기존 미들웨어의 기능을 확장할 수 있기 때문이다. 예를 들어 다음과 같이 분기 처리를 할 수도 있다.
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
morgan('combined')(req, res, next);
} else {
morgan('dev')(req, res, next);
}
});
조건문에 따라 다른 미들웨어를 적용하는 코드이다. 아래와 같이 작성하면 진행이 안된다.
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
morgan('combined');
} else {
morgan('dev');
}
})