Express 미들웨어 작성하기

Express 미들웨어 작성하기

Express 미들웨어는 요청-응답 사이클에서 request 객체(req), response 객체(res), 그리고 next 함수에 접근할 수 있는 함수예요. next 함수는 Express 라우터의 함수로, 호출되면 현재 미들웨어 다음에 오는 미들웨어를 실행해요.

출처: Writing middleware for use in Express apps · Express.js

미들웨어 함수는 다음 작업을 할 수 있어요.

  • 어떤 코드든 실행
  • request와 response 객체 변경
  • 요청-응답 사이클 종료
  • 스택의 다음 미들웨어 호출

중요: 현재 미들웨어 함수가 요청-응답 사이클을 끝내지 않는다면, 반드시 next()를 호출해 제어를 다음 미들웨어 함수로 넘겨야 해요. 그렇지 않으면 요청이 그대로 걸려 있어요(hanging).

Express 5부터, Promise를 반환하는 미들웨어 함수는 reject되거나 에러를 throw하면 next(value)를 호출해요. next는 rejected 값이나 throw된 Error로 호출돼요.

예제

"Hello World" Express 애플리케이션 예제로 시작해 볼게요. 이후 세 가지 미들웨어 함수를 추가할 거예요 — 로그 메시지를 출력하는 myLogger, HTTP 요청의 타임스탬프를 보여주는 requestTime, 그리고 들어오는 쿠키를 검증하는 validateCookies.

const express = require('express')
const app = express()

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(3000)

myLogger 미들웨어

요청이 통과할 때 "LOGGED"를 출력하는 myLogger란 미들웨어 함수예요. myLogger라는 변수에 할당돼요.

const myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

여기서 myLoggerapp.use()에 직접 전달되지 않고 별도로 정의됐기 때문에, TypeScript가 파라미터를 추론할 맥락이 없어 명시적으로 주석이 달려 있어요. 함수 전체를 RequestHandler로 타입 지정하면 req, res, next를 알아서 타입을 붙여줘요. app.use() 안에 인라인으로 작성하면 Express가 그 세 파라미터를 추론해서 주석이 필요 없어요.

주의: 위의 next() 호출을 주목해 보세요. 이 함수를 호출하면 앱의 다음 미들웨어 함수가 호출돼요. next() 함수는 Node.js나 Express API의 일부가 아니라, 미들웨어 함수에 전달되는 세 번째 인자예요. next() 함수는 어떤 이름이든 될 수 있지만 관례상 항상 "next"로 이름 지어요. 혼동을 피하려면 항상 이 관례를 따르세요.

미들웨어를 로드하려면 app.use()를 호출하고 미들웨어 함수를 지정해요.

app.use(myLogger)

app.get('/', (req, res) => {
  res.send('Hello World!')
})

이제 앱이 요청을 받을 때마다 "LOGGED"라는 메시지를 터미널에 출력해요. 미들웨어의 로딩은 정의된 순서로 이뤄져요. app.use(myLogger)가 루트(/) 라우트보다 앞에 있으므로, 요청이 들어올 때마다 myLogger가 먼저 실행돼요.

구성 가능한 미들웨어

미들웨어가 구성 가능하길 원한다면, options 객체나 다른 파라미터를 받는 함수를 export 하고, 그 함수가 입력 파라미터에 기반한 미들웨어 구현을 반환하게 해요.

module.exports = function (options) {
  return function (req, res, next) {
    // Implement the middleware function based on the options object
    next()
  }
}

이제 미들웨어는 이렇게 사용할 수 있어요.

const mw = require('./my-middleware.cjs')
app.use(mw({ option1: '1', option2: '2' }))

cookie-sessioncompression이 구성 가능한 미들웨어의 예시예요.

정리하면, 미들웨어는 (req, res, next)를 받는 함수고, 요청-응답 사이클을 끝내지 않으면 반드시 next()를 호출해야 해요. app.use()로 로드하고, 옵션을 받아 구성하는 팩토리 패턴도 가능해요.

더 알아보기