Express 애플리케이션의 뼈대는 결국 "어떤 URL로 들어온 어떤 HTTP 요청을 누가 처리하느냐"를 정의하는 일이다. 이 정의를 담당하는 것이 Routing이며, Express는 이를 위한 간결하면서도 유연한 API를 제공한다. Route를 잘 설계하면 코드 구조가 명확해지고 유지보수가 쉬워진다.
Express에서 Route는 HTTP Method, Route Path, Route Handler 세 요소로 구성된다.
app.<method>(<path>, <handler>)
──┬── ──┬── ───┬────
│ │ └─ 요청을 처리하는 함수 (req, res, next)
│ └─────────── 매칭할 URL 패턴
└─────────────────── get | post | put | patch | delete
Route Handler — (req, res, next) 시그니처를 가지는 함수. 여러 개를 배열 또는 나열로 등록하면 순서대로 실행된다.
import express from "express";
const app = express();
app.get("/users", (req, res) => {
res.json({ users: [] });
});
app.post("/users", (req, res) => {
// req.body로 전달된 데이터를 저장
res.status(201).json({ created: true });
});
경로의 일부를 동적 세그먼트로 선언할 때 :paramName 문법을 사용한다. 매칭된 값은 req.params 객체에서 꺼낸다.
app.get("/users/:userId/posts/:postId", (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
Route Parameter — URL 경로 세그먼트에 바인딩된 변수. /users/42/posts/7 요청 시 req.params.userId === "42", req.params.postId === "7".
?key=value 형식으로 전달되는 Query Parameter는 req.query로 접근한다. Route Path 정의와 무관하게 자동으로 파싱된다.
// GET /products?category=book&page=2
app.get("/products", (req, res) => {
const { category, page = "1" } = req.query;
res.json({ category, page: Number(page) });
});
| 구분 | 위치 | 접근 방법 | 용도 |
|---|---|---|---|
| Route Parameter | /users/:id | req.params.id | 리소스 식별 |
| Query Parameter | ?sort=asc | req.query.sort | 필터링, 정렬, 페이지네이션 |
규모가 커지면 Route를 도메인별로 분리하는 것이 좋다. express.Router로 Nested Router를 만들어 app.use에 마운트한다.
app
├── /users → usersRouter
│ ├── GET / → 목록 조회
│ ├── POST / → 생성
│ └── GET /:id → 단건 조회
└── /posts → postsRouter
├── GET / → 목록 조회
└── GET /:id → 단건 조회
// routes/users.ts
import { Router } from "express";
const router = Router();
router.get("/", (req, res) => res.json({ users: [] }));
router.post("/", (req, res) => res.status(201).json({ created: true }));
router.get("/:id", (req, res) => res.json({ id: req.params.id }));
export default router;
// app.ts
import usersRouter from "./routes/users";
import postsRouter from "./routes/posts";
app.use("/users", usersRouter);
app.use("/posts", postsRouter);
Nested Router — Router 인스턴스를 별도 모듈로 분리한 뒤 부모 앱에 마운트하는 패턴. /users에 마운트된 Router 내부의 /:id는 실제로 /users/:id로 동작한다.
Router 단위로 Middleware를 적용할 수도 있어, 인증 검사나 로깅 같은 공통 처리를 특정 경로 그룹에만 한정할 때 유용하다.