Express 애플리케이션에 HTTP 요청이 들어오면, 단순히 Handler 하나가 실행되는 것이 아니라 여러 층의 Middleware 체인을 순서대로 통과한다. 이 흐름을 정확히 이해해야 인증, 로깅, 에러 처리를 올바른 위치에 배치할 수 있다. 요청이 어디서 멈추고 어떻게 응답으로 이어지는지 단계별로 살펴본다.
Client
│
▼
HTTP Server (net.Server)
│
▼
app (Express Application)
│
├─ Application-level Middleware (app.use)
│ ├─ morgan, cors, body-parser, ...
│ └─ next() 호출 시 다음 단계로
│
├─ Router (express.Router)
│ ├─ 경로 매칭
│ └─ Router-level Middleware
│
├─ Route Handler (Controller)
│ └─ res.send / res.json / res.end
│
└─ Error-handling Middleware (err, req, res, next)
└─ 에러 응답 반환
Middleware — (req, res, next) 시그니처를 갖는 함수. next를 호출해 체인의 다음 함수로 제어를 넘긴다.
app.use로 등록된 Middleware는 등록 순서대로 실행된다. next를 호출하지 않으면 체인이 중단되고 이후 Handler는 실행되지 않는다.
import express from "express";
const app = express();
// 1번: 먼저 실행
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // 다음 Middleware로
});
// 2번: body 파싱
app.use(express.json());
// 3번: Route
app.get("/users/:id", (req, res) => {
res.json({ id: req.params.id });
});
express.json처럼 내장 Middleware도 app.use로 체인에 삽입된다. 순서가 곧 실행 순서다.
Router는 경로 접두사 단위로 Middleware와 Handler를 묶는 미니 애플리케이션이다.
// routes/users.ts
import { Router } from "express";
const router = Router();
router.use(authMiddleware); // /users/* 경로에만 적용
router.get("/", getUsers);
router.get("/:id", getUserById);
router.post("/", createUser);
export default router;
// app.ts
app.use("/users", usersRouter);
Router-level Middleware — router.use로 등록되며, 해당 Router에 매칭된 요청에만 적용된다.
Handler 또는 Middleware에서 next(err) 호출
│
▼
일반 Middleware 건너뜀
│
▼
Error-handling Middleware (인자 4개: err, req, res, next)
│
▼
에러 응답 반환
// 에러를 next로 전달
app.get("/data", async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (err) {
next(err); // Error-handling Middleware로 이동
}
});
// Error-handling Middleware — 반드시 인자 4개
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error(err.stack);
res.status(500).json({ message: err.message });
});
next(err)처럼 next에 인자를 넘기면 Express는 일반 Middleware를 모두 건너뛰고 인자가 4개인 Error-handling Middleware를 찾아 실행한다. 이 Middleware는 반드시 체인의 가장 마지막에 등록해야 한다.
| 메서드 | 용도 |
|---|---|
res.send | 문자열 또는 Buffer 응답 |
res.json | JSON 직렬화 후 응답 |
res.end | 바디 없이 응답 종료 |
res.redirect | 3xx 리다이렉트 |
응답 메서드가 한 번 호출되면 HTTP 응답이 전송된다. 이후 next를 호출하거나 응답 메서드를 다시 호출하면 Cannot set headers after they are sent 에러가 발생한다.