사이드 프로젝트를 하면서 Express를 사용하고 있다. 저번에 회사에서 개발할 때 Nest.js를 쓰면서 Strategy를 구현하고 이것이 Middleware랑 비슷하다고 생각했다.
Nest.js에서는 Strategy를 구현하면 어떻게 동작하게 되는지 이해가 갔지만 Express로 개발을 하다보니 어떻게 동작을 하는지 의문이 들어서 정리를 해보게 됐다.
Middleware는 request 객체, response 객체, 그리고 어플리케이션 요청-응답 사이클 도중에 그 다음 Middleware 함수에 대한 액세스 권한을 갖는 함수다.
간단하게 요약하자면 클라이언트에게 요청이 오면 그 요청을 보내려고 응답하려는 중간 목적에 맞게 거쳐가는 함수들이다.
Express의 Middleware는 HTTP 요청이 들어온 순간 순차적으로 실행이 된다. Middleware는 HTTP 요청과 응답 객체를 처리하거나, 다음 Middleware를 실행할 수 있다. 그리고 HTTP 응답이 끝날 때 까지 Middleware 동작 사이클이 실행된다.
router.use(auth); // 3
router.get('/', (req, res, next) => { // 4
res.send('Roter');
}
app.use((req, res, next) => { // 1
console.log(`Request ${req.auth}`);
next();
});
app.use('/main', router); // 2
위 코드를 보면 router 객체에 Middleware가 적용되는 것 외에 애플리케이션 Middleware와 사용 방법은 같다. 특정 경로의 라우팅에만 Middleware를 적용하기 위한 벙법으로 app 객체에 router가 적용된 이후로 순서대로 실행이 된다.
app.use(middleware1, middleware2, ...);
app.use('/main', auth, mainRouter);
app.get('/', logger, (req, res, next) => {
res.send('Express Start');
});
위 코드는 Middleware 여러 개를 동시에 적용을 하며, 주로 한 개의 경로에 특정정해서 Middleware를 적용하기 위해 사용된다. 순서는 전달된 인자의 순서 순으로 실행이 된다.
오류처리 Middleware는 일반적으로 가자 마지막에 위치하며 err, req, res, next
이 네 가지 인자를 갖고, 앞에 Middleware에서는 next method에 인자가 전달 되면 실행이 된다.
app.use((req, res, next) => {
if (!isAdmin(req)) {
next(new Error('Not Authorized')); // 1 중간은 건너뛰고 마지막 오류처리 Middleware 실행.
return;
}
next();
});
app.get('/', (req, res, next) => {
res.send('Express Start');
});
app.use((err, req, res, next) => { // 2 Error 인자
res.send('Error Occurred');
});
next(new Error());
가장 아래 적용된 err, req, res, next
를 인자로 갖는 함수가 오류처리 Middleware로 이전에 적용된 Middleware 중 next에 인자를 넘기는 경우엔 중간 Middleware를 건너고 오류처리 Middleware가 실행된다.
하나의 Middleware를 작성하고, 작동 모드를 선택하면서 사용하고 싶다면 Middleware를 함수형으로 작성해서 사용할 수 있다.
그리고 API별로 사용자 권한을 제한을 하고 싶을 땐 Middleware를 1개만 작성하고 사용자의 권한을 Middleware 인자로 넘겨주면 체크할 수 있는 로직이 만들어진다.
const auth = (role) => {
return (req, res, next) => {
if (!checkRole(req, role)) {
next(new Error(`User Is NOT ${role}`));
return;
}
next();
}
};
app.use('/admin', auth('admin'), adminRouter));
app.use('/users', auth('user'), usersRouter);
auth 함수는 Middleware 함수를 반환하는 함수로 auth 함수 실행 시 Middleware 동작이 결정되는 방식이다. 일반적으로 동일한 로직에 설정값만 다르게 Middleware를 사용하고 싶을 경우 활용된다.
이렇게 Middleware의 동작 관련해서 정리를 해보면서 역할에 따른 Middleware의 동작 원리를 알게 됐다. Middleware에 대해서 조금 더 요약을 해보자면
req, res, next
를 인자로 갖는 함수는 Middleware로 동작위에 리스트로 요약을 한 것과 같이 Middleware는 코딩을 할 때, 보통 함수형 Middleware를 많이 사용을 해왔던 것 같다.
이렇게 보니 코딩에는 정답이 없고 본인의 스타일에 맞춰서 사용한다면 더 좋은 코딩을 할 수 있을 거 같다.