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);
동작:
- extendedLogger가 호출되면, 내부에서 basicLogger를 먼저 실행한다.
- basicLogger가 로그를 남기고 next()를 호출.
- 다시 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 라우트에만 미들웨어 실행, 성능 ↑
- 단점: 모든 라우트에 동일 기능을 적용하려면 코드 중복 ↑
'Node.js' 카테고리의 다른 글
익스프레스 웹 서버 만들기 - dotenv (0) | 2025.01.15 |
---|---|
익스프레스 웹 서버 만들기 - 미들웨어(multer) (0) | 2025.01.15 |
익스프레스 웹 서버 만들기 - 미들웨어(session) (0) | 2025.01.14 |
익스프레스 웹 서버 만들기 - 미들웨어(express.static) (1) | 2025.01.14 |
익스프레스 웹 서버 만들기 - 미들웨어(morgan, cookieParser, express.json, express.urlencode) (0) | 2025.01.13 |