본문 바로가기
Node.js

익스프레스 웹 서버 만들기 - 미들웨어 확장법

by Programmer.Junny 2025. 1. 14.

1. 미들웨어 확장법

1.1 기본 개념

  • 미들웨어는 **요청(req)**과 응답(res) 사이에서 특정 로직을 수행하고, **next()**를 호출하여 다음 미들웨어로 제어를 넘긴다.
  • 확장이란, 기존 미들웨어가 제공하는 기능을 그대로 유지하면서, 추가적인 로직(기능)을 덧붙이거나서로 다른 미들웨어를 조합하는 방식을 의미한다.

1.2 확장 이유

  • 코드 재사용: 중복 없이 여러 기능을 합성하여, 유지보수가 쉬운 구조를 만든다.
  • 모듈화: 미들웨어를 작은 단위로 쪼개고, 필요한 부분만 골라쓰거나, 새로운 기능을 추가하기 유용하다.
  • 선택적 적용: 특정 조건에서만 추가 기능이 필요할 때, 조건부 로직으로 확장된 미들웨어를 사용할 수 있다.

2. 미들웨어 확장 방법

2.1 미들웨어 합성(Composition)

  • **합성(Composition)**은 여러 미들웨어를 순차적으로 연결해, 각각의 기능을 차례대로 실행하도록 하는 방식이다.

예시 1: 전역 등록

// 미들웨어 A
const middlewareA = (req, res, next) => {
  console.log('미들웨어 A');
  next();
};

// 미들웨어 B
const middlewareB = (req, res, next) => {
  console.log('미들웨어 B');
  next();
};

// 전역 미들웨어로 순차 적용
app.use(middlewareA);
app.use(middlewareB);

// 라우트
app.get('/', (req, res) => {
  res.send('홈페이지');
});
  • 동작: 요청이 들어올 때마다 A → B 순으로 실행되고, 이후 라우트 핸들러로 넘어간다.

예시 2: 라우트별 등록

app.get(
  '/admin',
  middlewareA,
  middlewareB,
  (req, res) => {
    res.send('관리자 페이지');
  }
);
  • 동작: /admin 라우트에 대해 요청이 들어올 때, A → B 순으로 실행한 뒤 라우트 핸들러가 호출된다.

2.2 미들웨어 래핑(Wrapping)

  • **래핑(Wrapping)**은 하나의 미들웨어가 기존 미들웨어를 내부에서 호출함으로써 추가 기능을 덧붙이는 방식이다.
  • 장점: 기존 미들웨어의 기능과 인터페이스를 그대로 유지하면서, 새로운 로직을 덧붙일 수 있다.

예시: 기존 로거 + 추가 로직

// 기본 로거
const basicLogger = (req, res, next) => {
  console.log(`Basic: ${req.method} ${req.url}`);
  next();
};

// 확장된 로거 (기존 basicLogger를 내부에서 호출)
const extendedLogger = (req, res, next) => {
  // 기존 로거 호출
  basicLogger(req, res, () => {
    const start = Date.now();

    res.on('finish', () => {
      const duration = Date.now() - start;
      console.log(`Extended: ${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`);
    });
    
    next();
  });
};

// 사용
app.use(extendedLogger);

동작:

  1. extendedLogger가 호출되면, 내부에서 basicLogger를 먼저 실행한다.
  2. basicLogger가 로그를 남기고 next()를 호출.
  3. 다시 extendedLogger로 돌아와, 응답 완료 시 추가적인 로그(duration 등)를 남긴 뒤 next()를 호출한다.

2.3 조건부 미들웨어 실행

  • 조건부 로직에 따라 특정 미들웨어를 실행할지 말지 결정하는 방식이다.
  • 필요한 요청에만 특정 미들웨어를 적용하여 성능 최적화와 보안을 강화한다.

예시: API 경로만 로깅

const conditionalMiddleware = (condition, middleware) => {
  return (req, res, next) => {
    if (condition(req)) {
      middleware(req, res, next);
    } else {
      next();
    }
  };
};

const apiLogger = (req, res, next) => {
  console.log(`[API LOG] ${req.method} ${req.url}`);
  next();
};

// /api 경로에만 로깅 적용
app.use(
  conditionalMiddleware(
    (req) => req.url.startsWith('/api'),
    apiLogger
  )
);

2.4 미들웨어 팩토리(Middleware Factory)

  • 매개변수를 받아 커스터마이징된 미들웨어를 동적으로 생성하는 방식이다.

예시: 역할(roles)에 따른 접근 제어

const hasRole = (role) => {
  return (req, res, next) => {
    if (req.session.user && req.session.user.role === role) {
      next();
    } else {
      res.status(403).send('권한이 없습니다.');
    }
  };
};

// 관리자 전용 라우트
app.get('/admin', hasRole('admin'), (req, res) => {
  res.send('관리자 페이지');
});
  • 동작: hasRole('admin')이 미들웨어를 반환하고, 라우트에서 이 미들웨어를 호출해 권한을 검증한다.

3. 전역 등록 vs. 라우트별 등록

// 전역 등록 (app.use)
app.use(basicLogger);
app.use(extendedLogger);

app.get('/admin', (req, res) => {
  res.send('관리자 페이지');
});

 

  • 장점: 모든 라우트에 대해 로그가 찍히므로 일관성 ↑
  • 단점: /admin 외의 불필요한 라우트에서도 extendedLogger가 실행될 수 있음
// 라우트별 등록
app.get('/admin', basicLogger, extendedLogger, (req, res) => {
  res.send('관리자 페이지');
});

 

  • 장점: /admin 라우트에만 미들웨어 실행, 성능 ↑
  • 단점: 모든 라우트에 동일 기능을 적용하려면 코드 중복 ↑

최근댓글

최근글

skin by © 2024 ttuttak