on1ystar 머릿속을 정리, 기록하는 곳

Node.js/Express 미들웨어


의문점이나 지적 등의 관심 및 조언을 위한 댓글이나 메일은 언제나 환영이고 감사합니다.


☄ About middleware


미들웨어(middleware)는 요청과 응답의 중간에 위치해 기능을 추가하거나, 원치 않는 요청을 걸러내는 등 매우 다양하다. 라우터나 에러 핸들러 또한 미들웨어의 일종이다. 사실 핸들러들 중 미들웨어와 컨트롤러 등을 구분하는 기준은 문법적인 것 보다는 의미적인 것이 크다.

const handleHome = (req, res) =>
  res.send("This is the first home for Express.");

app.get("/", handleHome);

app.listen(PORT, () =>
  console.log(`✅ Server listening on http://127.0.0.1:${PORT}/`)
);

예를 들어 위 handleHome'/' 경로로 오는 요청을 받아 응답하는 컨트롤러다. 단, 모든 컨트롤러는 미들웨어가 될 수 있다. 미들웨어로 사용하는 함수는 매개변수를 req, res 외에 3번째로 next를 가진다. (첫 매개변수로 err을 추가하면 에러 처리 미들웨어로 사용할 수 있음. 그리고 이 변수들의 변수 명은 전혀 상관이 없고 순서가 중요함.)

const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}.`);
  next();
};
const handleHome = (req, res) =>
  res.send("This is the first home for Express.");

app.get("/", logger, handleHome);
  • req.method : 요청된 http mothod
  • req.url : 요청된 url

미들웨어를 콜백으로 등록하는 방법은 get두 번째 인자부터 차례대로 입력해 주면 된다. 사실 Application.get() 메소드는 인자 값으로 여러 개의 핸들러를 가질 수 있다.

그럼 여러 개의 핸들러를 알아서 get 함수가 실행시켜 주는가? 완전히 그렇지는 않다. 먼저 가장 첫 번째 핸들러(두 번째 인자 값)를 콜백으로 호출시킨다. 그럼 호출된 핸들러에서 여러 로직을 처리한 다음 두 번째 핸들러(위 에서는 handleHome 컨트롤러)를 호출하기 위해서는 미들웨어의 3번 째 인수인 next를 사용해 next()와 같이 호출한다.

중요한 것은 순서다. 각 미들웨어의 목적에 맞게 요청을 받아 응답 처리를 하는 핸들러(컨트롤러) 전에 호출되야 하므로 그 앞에 넣어준다. 단, 미들웨어가 목적 상 로직을 처리한 다음 응답까지 하기도 한다.

위 예제에서는 /로 접속하면, getloggerhandleHome 순서로 처리된다.

handleHome 컨트롤러에 next를 추가할 수 있지만 역할 상 마지막에 호출되기 때문에 필요 없다.

미들웨어는 보통 global 영역으로 사용하기 위해 app.use()와 함께 사용된다.

다시 한 번 강조하자면 이 미들웨어를 사용할 때는 순서가 매우 중요하다. 일반적으로 위에서 아래로 한 줄씩 실행되는 자바스크립트의 특성을 그대로 이어 받아 app.use()가 선언된 위치에 따라 실행 되고, 안되고가 결정될 수 있다.

app.use((req, res, next) => {
  console.log("global middleware");
  next();
});

app.get("/", (req, res) => res.send("Home")); // global middleware 로그가 찍힘
app.get("/login", (req, res) => res.send("Login")); // global middleware 로그가 찍힘
app.get("/", (req, res) => res.send("Home")); // global middleware 로그가 안 찍힘
app.use((req, res, next) => {
  console.log("global middleware");
  next();
});
app.get("/login", (req, res) => res.send("Login")); // global middleware 로그가 찍힘

특정 url 요청에만 실행되는 미들웨어를 사용하고 싶으면 첫 번째 인자에 url을 지정해 주면 된다.

app.use("/", (req, res, next) => {
  console.log("global middleware");
  next();
});

app.get("/", (req, res) => res.send("Home")); // global middleware 로그가 찍힘
app.get("/login", (req, res) => res.send("Login")); // global middleware 로그가 안 찍힘

여기서 주의해야 할 점은 next()를 해주지 않으면 다음 함수를 호출하지 않으므로 응답을 보내지 못해 일종의 block 상태가 된다. 예를 들어 위 코드에서 next()를 생략하면 /요청에 res.send("Home")응답을 보내지 못해 빈 화면에서 로딩 중이 반복된다.

이를 활용해 간단하게 로깅을 확인할 logger와 특정 url 접속을 차단하는 privateMiddleware를 만들어 볼 수 있다.

const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}.`);
  next();
};
const privateMiddleware = (req, res, next) => {
  const url = req.url;
  if (url === "/protected") {
    console.log("🚨 Someone tried to connect to private lounge.");
    res.send("<h1>Not Allowed</h1>");
  }
  next();
};

const handleHome = (req, res) =>
  res.send("This is the first home for Express.");
const handleProtected = (req, res) =>
  res.send("Welcome to the private lounge.");

app.use(logger);
app.use("protected", privateMiddleware);
app.get("/", handleHome);
app.get("protected", handleProtected);
  • /로 접속 시도 시,

loggerhandlehome

  • /protected로 접속 시도 시,

loggerprivateMiddleware (handleProtected는 호출되지 않음)


☄ 자주 사용하는 미들웨어


npm에는 훌륭한 미들웨어들이 많기 때문에 적극적으로 이용하는 것이 좋다.

morgan

node.js용 HTTP 요청 로거 미들웨어로 위에서 만든 logger보다 다양하고 정교한 기능들을 가지고 있어 로깅을 위한 미들웨어로 많이 사용된다.

다양한 옵션이 있으므로 문서에서 참고

http://expressjs.com/en/resources/middleware/morgan.html

  • npm install morgan
// ...
import morgan from "morgan";

// ...

const logger = morgan("dev"); // 5가지 옵션 중 dev 사용

const handleHome = (req, res) =>
  res.send("This is the first home for Express.");

app.use(logger);
app.get("/", handleHome);

// ...

순서대로 HTTP method, route, status code, 응답시간

이 외에도 앞으로 사용할 미들웨어들을 간단히 소개만 해 보면,

body-parser

body 요청의 구문 분석, 특히 사용자의 입력 파싱에 도움을 줌.

http://expressjs.com/en/resources/middleware/body-parser.html

  • npm install body-parser

쿠키 헤더를 구문 분석하고 쿠키 이름으로 키 지정된 개체로 req.cookie를 채움

http://expressjs.com/en/resources/middleware/cookie-parser.html

  • npm install cookie-parser

helmet

다양한 보안 관련 미들웨어

https://www.npmjs.com/package/helmet

  • npm install helmet


Reference

Node.js 교과서(2판) - 조현영

https://nomadcoders.co/wetube