🛠️ 처음부터 만드는 Middleware — 요청을 레이어별로 가공하기
문제
HTTP 요청을 처리할 때 인증, 로깅, 에러 핸들링 같은 공통 로직을 매번 복붙하고 계신가요?
Middleware 패턴을 사용하면 각 관심사를 독립적인 레이어로 분리하고, 체인처럼 연결할 수 있습니다.
직접 만들어 봅시다
```typescript
type Context = { req: Request; res: Record
type Next = () => Promise
type Middleware = (ctx: Context, next: Next) => Promise
class MiddlewareRunner {
private stack: Middleware[] = [];
use(fn: Middleware): this {
this.stack.push(fn);
return this;
}
async run(ctx: Context): Promise
let index = -1;
const dispatch = async (i: number): Promise
if (i <= index) throw new Error('next() called multiple times');
index = i;
const fn = this.stack[i];
if (!fn) return;
await fn(ctx, () => dispatch(i + 1));
};
await dispatch(0);
}
}
```
사용 예제
```typescript
const app = new MiddlewareRunner();
// 1) 로깅 미들웨어
app.use(async (ctx, next) => {
const start = Date.now();
console.log(`→ ${ctx.req.method} ${ctx.req.url}`);
await next();
console.log(`← ${Date.now() - start}ms`);
});
// 2) 인증 미들웨어
app.use(async (ctx, next) => {
const token = ctx.req.headers.get('authorization');
if (!token) {
ctx.res = { status: 401, body: 'Unauthorized' };
return; // next()를 호출하지 않으면 체인이 여기서 멈춤
}
ctx.state.user = { id: 1, name: 'hyuk' };
await next();
});
// 3) 비즈니스 로직
app.use(async (ctx, next) => {
ctx.res = { status: 200, body: `Hello, ${ctx.state.user.name}!` };
await next();
});
// 실행
const ctx = {
req: new Request('https://api.example.com/hello', {
headers: { authorization: 'Bearer abc123' },
}),
res: {},
state: {},
};
await app.run(ctx);
console.log(ctx.res); // { status: 200, body: 'Hello, hyuk!' }
```
핵심 포인트
| 개념 | 설명 |
|------|------|
| 양파 모델 | `next()` 호출 전 = 요청 처리, 후 = 응답 처리. 로깅 미들웨어가 시간을 잴 수 있는 이유 |
| 체인 중단 | `next()`를 호출하지 않으면 이후 미들웨어가 실행되지 않음 (인증 실패 시 유용) |
| 중복 호출 방지 | `index` 추적으로 같은 미들웨어에서 `next()`를 두 번 호출하면 에러 발생 |
| 순서가 중요 | `use()` 순서대로 실행 — 로깅 → 인증 → 비즈니스 로직 |
실전에서는?
Express, Koa, Hono 등 거의 모든 Node.js 프레임워크가 이 패턴을 사용합니다. 특히 [Koa](https://koajs.com/)의 미들웨어 구현이 위 코드와 거의 동일합니다.
Next.js의 [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)도 같은 개념이지만, Edge Runtime에서 라우팅 전에 실행되는 단일 레이어로 동작합니다.
참고 문서:
Comments (0)
💬
No comments yet.
Be the first to comment!