개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Express Request Lifecycle — 요청이 처리되는 전체 흐름

2025년 2월 5일
ExpressRequest LifecycleMiddlewareRouterController

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를 호출해 체인의 다음 함수로 제어를 넘긴다.

Middleware 체인 동작 방식

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와 경로 매칭

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.jsonJSON 직렬화 후 응답
res.end바디 없이 응답 종료
res.redirect3xx 리다이렉트

응답 메서드가 한 번 호출되면 HTTP 응답이 전송된다. 이후 next를 호출하거나 응답 메서드를 다시 호출하면 Cannot set headers after they are sent 에러가 발생한다.

← 이전 글Express Template Engine — 서버사이드 HTML 렌더링
다음 글 →Express 프로젝트 구조 — MVC와 Layered 패턴