미들웨어 사용하기

미들웨어 사용하기 (Using Middleware)

Express는 자체 기능이 최소한인 라우팅 및 미들웨어 웹 프레임워크예요. Express 애플리케이션은 본질적으로 요청-응답 사이클 동안 실행되는 일련의 미들웨어 함수 호출이라고 볼 수 있지요. 미들웨어 함수는 요청 객체(req), 응답 객체(res), 그리고 사이클의 다음 미들웨어 함수(next)에 접근할 수 있어요.

출처: Express 공식 문서 - Using middleware

미들웨어 함수는 다음 일을 할 수 있어요: 코드를 실행하고, 요청·응답 객체를 수정하고, 요청-응답 사이클을 종료하고, 다음 미들웨어 함수에 제어를 넘깁니다. 만약 미들웨어가 사이클을 종료하지 않는다면 반드시 next()를 호출해 다음 미들웨어에 제어를 넘겨야 해요. 그렇지 않으면 요청이 계속 대기 상태로 남아요.

Express 애플리케이션은 애플리케이션 레벨, 라우터 레벨, 오류 처리, 내장, 서드파티 미들웨어 다섯 종류를 사용할 수 있어요.

애플리케이션 레벨 미들웨어

app.use()app.METHOD() 함수로 앱 객체 인스턴스에 바인딩해요. 여기서 METHOD는 미들웨어가 처리하는 요청의 소문자 HTTP 메서드(get, post, put, delete 등)예요.

마운트 경로 없이 정의한 미들웨어는 앱이 요청을 받을 때마다 실행돼요:

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

app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

경로에 마운트한 미들웨어는 해당 경로의 모든 HTTP 요청 타입에 대해 실행돼요:

app.use('/user/:id', (req, res, next) => {
  console.log('Request Type:', req.method);
  next();
});

미들웨어 서브 스택이라는 게 있어요. 여러 미들웨어 함수를 마운트 지점에 함께 로드해서 하나의 스택을 만들 수 있지요:

app.use(
  '/user/:id',
  (req, res, next) => {
    console.log('Request URL:', req.originalUrl);
    next();
  },
  (req, res, next) => {
    console.log('Request Type:', req.method);
    next();
  }
);

라우터 미들웨어 스택의 나머지 미들웨어를 건너뛰고 다음 라우트에 제어를 넘기려면 next('route')를 호출해요. 이는 app.METHOD()router.METHOD()로 로드한 미들웨어에서만 동작한다는 점을 기억하세요.

app.get(
  '/user/:id',
  (req, res, next) => {
    // 사용자 ID가 0이면 다음 라우트로 건너뜁니다
    if (req.params.id === '0') next('route');
    // 그 외에는 이 스택의 다음 미들웨어로 제어를 넘깁니다
    else next();
  },
  (req, res) => {
    // 일반 응답을 보냅니다
    res.send('regular');
  }
);
// /user/:id 경로에 대해 특별 응답을 보내는 핸들러
app.get('/user/:id', (req, res) => {
  res.send('special');
});

미들웨어 함수를 배열로 묶어 재사용성을 높일 수도 있어요:

function logOriginalUrl(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}
function logMethod(req, res, next) {
  console.log('Request Type:', req.method);
  next();
}
const logStuff = [logOriginalUrl, logMethod];

app.get('/user/:id', logStuff, (req, res) => {
  res.send('User Info');
});

라우터 레벨 미들웨어

라우터 레벨 미들웨어는 애플리케이션 레벨과 동일하게 동작하지만, express.Router() 인스턴스에 바인딩된다는 점이 달라요. router.use()router.METHOD() 함수로 로드하고, next('router')를 호출하면 라우터의 나머지 미들웨어를 건너뛰고 라우터 인스턴스 밖으로 제어를 넘겨요.

예를 들어 요청에 x-auth 헤더가 있을 때만 라우터가 응답하고, 그 외에는 next('router')로 라우터를 빠져나와 앱이 401 상태로 응답하게 만들 수 있어요:

const express = require('express');
const app = express();
const router = express.Router();

// 라우터에 조건을 걸고 필요하면 빠져나옵니다
router.use((req, res, next) => {
  if (!req.headers['x-auth']) return next('router');
  next();
});

router.get('/user/:id', (req, res) => {
  res.send('hello, user!');
});

// 라우터를 사용하고, 통과하는 요청은 401 처리
app.use('/admin', router, (req, res) => {
  res.sendStatus(401);
});

오류 처리 미들웨어

오류 처리 미들웨어는 다른 미들웨어와 같은 방식으로 정의하되, 인자가 3개가 아니라 4개 (err, req, res, next)라는 점이 달라요. 반드시 4개의 인자를 제공해야 오류 처리 미들웨어로 인식돼요. next 객체를 쓰지 않더라도 시그니처를 유지하기 위해 지정해야 해요. 그렇지 않으면 일반 미들웨어로 해석되어 오류를 처리하지 못해요.

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

내장 미들웨어

Express는 다음 내장 미들웨어 함수를 제공해요.

  • express.static — HTML 파일, 이미지 같은 정적 자산을 제공
  • express.json — JSON 페이로드가 있는 요청을 파싱
  • express.raw — Buffer 페이로드가 있는 요청을 파싱
  • express.text — 텍스트 페이로드가 있는 요청을 파싱
  • express.urlencoded — URL-encoded 페이로드가 있는 요청을 파싱

서드파티 미들웨어

필요한 기능의 Node.js 모듈을 설치한 뒤 애플리케이션 레벨이나 라우터 레벨에서 로드해 사용해요. 예를 들어 쿠키 파싱 미들웨어 cookie-parser를 설치하고 로드하는 경우입니다:

npm install cookie-parser
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

// 쿠키 파싱 미들웨어 로드
app.use(cookieParser());

더 알아보기