topic난이도 · 약 25

서버 요청 수명주기 — listen부터 close까지

listen → accept → 파싱 → 라우팅 → 미들웨어 → 핸들러 → 응답 → close. 모든 웹 서버의 공통 뼈대.

#서버#수명주기#미들웨어#우아한종료
왜 배우는가

Next.js·Express·FastAPI·Django — 이름만 다를 뿐 골격은 같다. 이 공통 흐름을 알면 어느 프레임워크든 빠르게 적응한다.

서버는 24시간 편의점 점원이다. 문을 열고(`listen`) 손님을 맞이하고(`accept`) 주문 내용을 파악(파싱)한 뒤, 어느 메뉴인지 고르고(라우팅), 재료 준비(미들웨어: 인증·로깅·CORS)를 거쳐 요리(핸들러)한 뒤 내주고(응답) 손님이 떠난다(close).

요청 수명주기 — TCP 연결 → HTTP 파싱 → 라우팅 → 미들웨어 체인 → 핸들러 → 응답
typescript
// 바닐라 Node.js — 가장 낮은 계층에서 본 서버
import http from "node:http";

const server = http.createServer((req, res) => {
  // 1) 파싱은 Node가 대신 해줌 (req.method, req.url, req.headers)
  console.log(`${req.method} ${req.url}`);

  // 2) 라우팅 — 수동
  if (req.url === "/" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello");
  } else if (req.url === "/api/users" && req.method === "POST") {
    // 3) 본문 스트리밍 수신
    let body = "";
    req.on("data", chunk => body += chunk);
    req.on("end", () => {
      const data = JSON.parse(body);
      res.writeHead(201, { "Content-Type": "application/json" });
      res.end(JSON.stringify({ id: Date.now(), ...data }));
    });
  } else {
    res.writeHead(404);
    res.end();
  }
});

server.listen(3000, () => console.log("listening 3000"));

// 우아한 종료 — SIGTERM 받으면 새 연결 거부, 기존 처리 완료 대기
process.on("SIGTERM", () => server.close(() => process.exit(0)));

Express/Next.js가 숨겨주는 것: 메서드·URL 파싱, 쿠키 파싱, JSON body 파싱, 에러 처리. 내부에는 이 바닐라가 돈다.

미들웨어(Middleware)는 요청·응답 사이에 끼워넣는 파이프라인 단계. 로깅·인증·CORS·에러 처리가 전부 미들웨어. Express/Koa/Next.js 공통 패턴.

typescript
// Express 스타일 미들웨어 체인
app.use(logger);                  // 1) 로깅 (모든 요청)
app.use(cors());                  // 2) CORS 헤더 부착
app.use(express.json());          // 3) JSON body 파싱
app.use("/admin", requireAuth);   // 4) /admin 하위에만 인증

app.get("/api/users", async (req, res, next) => {
  try {
    const users = await db.users.findAll();
    res.json(users);
  } catch (err) {
    next(err);                    // 에러 미들웨어로 위임
  }
});

app.use((err, req, res, next) => {   // 5) 에러 미들웨어 (4개 인자로 식별)
  console.error(err);
  res.status(500).json({ error: "Internal" });
});

"요청 흐름"을 다이어그램으로 그릴 줄 알면 디버깅이 쉬워진다. 어느 미들웨어에서 막혔는지 로그를 추가해 추적.

우아한 종료(Graceful Shutdown). 배포 시 서버를 내릴 때 진행 중 요청은 끝까지 처리하고 새 연결만 거부. SIGTERM 수신 → `server.close()` → 기존 요청 완료 대기 → 종료. 안 하면 사용자 요청이 503으로 끊긴다.

Next.js App Router의 route handler는 이 수명주기를 함수 한 개로 추상화한 것이다. `export async function GET(request) { return Response.json(...) }` 안쪽에서 Vercel/Node가 위 모든 단계를 대신 처리한다.

실기 드릴 2문항
edit실기 드릴 · 단답형

요청과 핸들러 사이에 끼워 넣어 로깅·인증·CORS 등을 처리하는 구성 요소의 이름은?

edit실기 드릴 · 단답형

배포 시 진행 중 요청을 끝까지 처리하고 새 연결만 막는 종료 방식을 뭐라고 부르는가?