본문 바로가기

javascript/nodejs

[express] middleware

Express는 미들웨어를 중심으로 동작한다. 이때 미들웨어는 주어진 요청에 대해 응답한 후 다음 선언된 미들웨어에게 순서를 넘기는 형식의 함수로, 아래와 같은 구조를 가진다. 

const middleware : RequestHandler = 
	(req : Request, res : Response, next : NextFunction) {
	//do something
};
  • req : HTTP Request에 대응되는 객체로, path · param · query 등의 다양한 정보를 파싱하여 제공한다. 
  • res : HTTP Response에 대응되며, send~ · render · end · redirect 등의 메서드를 통해 정보를 반활할 수 있다.
  • next : 다음 미들웨어를 호출하는 메서드이다.

미들웨어는 express를 통해 생성한 app 객체 혹은 Router 을 통해 사용할 수 있다.

//01. app 에서의 사용
const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(express.static(join('public')));

//02. Router 에서의 사용
const router = express.Router();

router.use((req,res,next) => {
	console.log("Hello, world!");
    next(); // 다음 미들웨어를 실행하기 위해서는 필수 !
};

router.get('/test1', middleware1);
router.post('/test2', middleware2);
// router의 use, get, post 등에서 사용 가능

app.use('/test', router);

app.listen(3000);

 

 위 예시에서는 app의 use 및 router 객체의 use, get, post 등을 통해 미들웨어를 사용하고 있다. 이때 get, post 등은 HTTP 메서드와 대응되는 메서드로, use와는 달리 대응되는 메서드를 통해 요청된 건에 대해서만 대응한다. 따라서 하나의 경로를 통해 다양한 메서드를 대응할 수 있다.


res : Response

 res는 앞서 언급했듯이 HTTP Response에 대응된다. 만약 res을 통해 접근 가능한 함수들을 사용하는 경우 HTTP 요청을 보낸 클라이언트에게 응답을 보내게 되는데, 이 경우 이후 순서의 미들웨어들은 실행되지 않는다. 응답을 보낸 이후에는 당연히 next( ) 함수도 작동하지 않는다.

next : NextFunction

 next는 다음 미들웨어를 호출하기 위해 사용되는 함수이다. 사용법은 매우 간단한데, 함수를 빠져나갈 때 return을 사용하듯이, 다음 미들웨어로 넘어가고 싶을 때 next 함수를 실행하면 된다.

 미들웨어 함수의 내부 구현에는 정해진 사항이 없으며, use 메서드에 경로를 따로 지정하지 않으면 모든 경로에 대응된다는 특성이 존재하기 때문에 외부 데이터를 가공하거나 로그를 남기는 등 다양한 목적으로 미들웨어를 사용할 수 있다.

 대부분의 express 기반 프로젝트에서 사용하는 urlencoded 및 static 역시 미들웨어로 구현되어 있다.


use의 특성 : 위에서 아래로 실행한다

 use를 이용한 미들웨어들은 선언된 순서대로, 즉 위에서 아래 순서로 차례대로 평가된다. 경로(path)의 경우, 일부 겹치는 부분이 있다면 라우팅이 실행된다. 이때 res을 통해 클라이언트에게 응답을 보내면 이후 미들웨어는 실행되지 않는다는 특성이 겹쳐, 하위 경로( 좀더 자세한 경로 ) 를 포함하는 라우팅이 더 아래에 존재하는 경우 react-router v5에서처럼 하위 경로에는 도달하지 못한다.

위 예시에서는 동일한 경로에 대해 요청하되, 단순히 경로를 선언한 순서만 바꾼 상황이다.

 첫번째의 경우 '/test' 경로에 대한 라우팅이 먼저, 이후 더 자세한 주소인 '/test/:pp'에 대한 라우팅이 설정되었다. 이때 use 메서드에 의한 경로 탐색은 상위에서 하위로 발생하기 때문에 우리가 찾는 경로 /test/13 은 첫번째 경로로 라우팅된다. 따라서 "상위 경로 테스트" 를 출력하고 있다.

 반면 두번째의 경우 위 상황과 달리 '/test/:pp' 경로가 더 상위에 선언되어 있으므로, 정상적으로 원하는 경로에 대해 라우팅할 수 있었고, 이에 의해 "하위 경로 테스트" 가 성공적으로 출력되고 있다.

 use 메서드는 위와 같이 먼저 선언된 경로 순서로 라우팅을 진행하기 때문에, 의도와는 다른 결과가 발생할 수 있다. 반면 get, post 등의 메서드는 경로가 완벽하게 일치하는 경우에만 라우팅을 진행한다. 

/test 경로가 /test/:pp 경로를 가리지 않는다. exact match 방식을 사용하는 것으로 보인다.
/test/13에 대응되는 경로가 없으므로, not found 경로를 반환했다.

 위 예시에서는 get 메서드를 이용하였다. get 메서드를 이용하는 경우 use와는 달리 '/test/:pp' 경로를 찾아가는 모습을 보였으며, 해당 경로가 없는 경우 하위에 지정해 두었던 not found 페이지를 출력했다.

 따라서 특정 경로를 라우팅할 때는 use 보다는 get, post 등의 메서드를 사용하는 편이 더 좋다.