프로그래밍/Node.js

미들웨어란? app.use()와 app.get() 사용법

상수나무 2022. 6. 28. 21:36

middleware 공식문서

Writing middleware for use in Express apps

 

Writing middleware for use in Express apps

Writing middleware for use in Express apps Overview Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a func

expressjs.com

*노마더코드 "유튜브 클론코딩" 강의를 들으면서 미들웨어에 대해 이해하기 위해 작성한 글이다. 

1. 미들웨어란?

공식 문서에 나와있는 middleware 설명은 다음과 같다.

Middleware functions are functions that have access to the request object(req), the response object(res), and the next function in the application’s request-response cycle.

 

미들웨어 함수는 request-response 사이클 도중에 요청객체(req), 응답객체(res), 그 다음 함수(next)에 접근할 수 있는 함수이다. + 그리고 next함수는 현재 미들웨어 뒤에 이어지는 다음 함수를 실행하는 함수이다.

 

즉, 미들웨어 함수는 라우터가 요청을 받아들이고 나서 응답하기 전에 특정기능을 수행하는 함수라고 할 수 있다.
예를 들어 해당 라우터가 유효한 url인지 확인하거나 로그인 여부를 확인하거나 하는 기능들을 넣을 수 있다.

 

말로만 들으면 어렵게 느껴지므로 코드로 예시를 들어보면 다음과 같다.

※ 이해하기 쉽게 request-response 사이클을 종료시키는 함수를 finalware라고 할 것이다.

import express from "express"
const PORT = 4000; // 1. request를 받을 포트번호를 지정한다.
const app = express(); // 2. express 앱을 하나 만든다.

const gossipMiddleware = (req, res, next) => { // 5. 미들웨어 함수
  console.log("I'm in the middle");
  next();
};

const handleHome = (req, res) => { // 6. 파이널웨어 함수
  return res.end(); 
};

app.get("/", gossipMiddleware, handleHome); // 4. 지정 router에 해당하는 request를 받는다.

// 3. 지정한 포트에서 request를 받을 수 있도록 설정한다. 
const handleListen = () => {
	console.log("Server listening...");
}
app.listen(PORT, handleListen)
  1. app.get()
    여러 개의 handler 함수를 추가할 수 있고, 순차적으로 함수를 실행한다.

    함수를 실행하면 “/” 라우터로 요청을 받아오고 gossipMiddleware 함수를 실행한다.
  2. gossipMiddleware
    next() 로 다음 handler함수인 handleHome 함수를 실행하게 한다. → 미들웨어
  3. handleHome
    return res.end() 로 요청-응답 사이클을 종료시킨다. → 파이널웨어

지금까지는 app.get() 함수 안에 미들웨어 함수를 넣어 특정 라우터를 대상으로 실행하는 방법을 배웠다. 이제 모든 라우터에서 작동하는 미들웨어를 만들어보자.

2. app.use()

app.use() 함수는 global middleware를 만들 수 있게 해주는 함수이다.
즉, 모든 url에서 동작하는 middleware를 만든다는 의미이다.

문법은 다음과 같다.

app.use(middleware함수)

해당 코드를 app.get() 함수 위쪽에 작성하면 모든 url에 대하여 지정한 미들웨어 함수를 실행한다. 그리고 미들웨어 함수 안에서는 next()를 이용해 다음 함수(코드)를 실행하도록 한다.

 

주의할 점
※ app.use()로 global middleware를 만들고 app.get()으로 라우터 요청을 받을 경우 코드의 순서를 잘 지켜야 한다. 모든 url에 대하여 먼저 미들웨어 함수를 실행시키고 싶다면 app.use() → app.get()의 순서로 코드를 작성해야 한다.

그 이유는 다음 코드에서 "/protected" 라우터를 처리하는 방식을 보면 이해가 쉽다. 미들웨어를 사용해 request를 요청한 url이 유효한지 검증하는 프로그램이다.

const logger = (req, res, next) => { // 2. logger 미들웨어에 있음을 알린다. 
  console.log(`I'm in logger`);
  next();
};
const privateMiddleware = (req, res, next) => { // 4. 유효한 페이지인지 검증한다.
  const url = req.url;
	// "/protected" 라우터면 next()하지 않고 요청을 종료시킨다.
  if (url === "/protected") { 
    return res.send("<h1>Not allowed</h1>");
  }
  console.log("Allowed, you may continue.");
  next();
};

const handleHome = (req, res) => {
  return res.end(); // request를 종료시킴
};

const handleProtected = (req, res) => {
  return res.send("Welcome to the private lounge.");
};

app.use(logger); // 1. logger 미들웨어 실행
app.use(privateMiddleware); // 3. privateMiddleware 미들웨어 실행

**app.get("/", handleHome); // 5. "/" 라우터에서 요청을 받을 시 실행 
app.get("/protected", handleProtected); // 6. "/protected" 요청을 받을 시 실행
  1. privateMiddleware
    “/protected”는 유효하지 않은 페이지로 간주하고 request를 종료시켜 버린다.

    그렇지않을 경우에는 next()함수를 실행하여 다음 코드를 실행하도록 한다.

  2. 오해의 소지
    app.use(privateMiddleware); 코드 밑에 app.get("/protected", handleProtected); 코드가 있다.

    여기에서 오해가 생길 수 있다.

    Q: 코드는 순차적으로 실행되므로 “/protected” 라우터로 요청을 보낸 경우, app.use()에서 먼저 privateMiddleware 를 실행하고 그 다음줄 app.get()을 실행하면 “/protected” 라우터로 요청을 받고 handleProtected 함수를 실행하는가?

    A: No!

    → 일단 “/protected” 라우터로 요청을 받으면 app.get()보다 먼저 app.use()로 privateMiddleware 를 실행하고, 이때 url이 “/protected”이므로 페이지에 “Not allowed” 문자를 출력하고 요청을 종료시켜버린다. 이때 “/protected”에 대한 요청이 종료되므로 app.get()으로 “/protected”요청을 받을 수 없다. 따라서 handleProtected 함수도 실행될 수 없다.


    즉, 미들웨어를 이용해 유효하지 않은 페이지의 경우에는 아예 app.get()을 할 수 없도록 사전에 차단하도록 한 것이다.

  3. 그렇다면 라우터 지정을 하지 않은 url로 요청하는 경우에는?
    예를 들어 위의 코드를 실행시키고 “http://localhost:4000/hello” 페이지로 들어간 경우, logger미들웨어를 실행하고 next()를 통해 privateMiddleware 를 실행한 뒤, next()함수를 실행하지만 “/hello” 라우터의 요청을 받는 get함수가 없으므로 페이지에는 Cannot GET /hello 에러가 뜨게 된다.