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

© 2026 newgirok

← 글 목록

Express Router — 라우터를 분리하는 방법

2025년 1월 28일
ExpressRouterexpress.RouterRouter 분리Modular Routing

Express 앱이 커질수록 app.get, app.post가 index.ts 한 파일에 쌓이면서 코드가 빠르게 뒤엉킨다. express.Router는 라우트를 독립 모듈로 분리해 각 도메인이 자신의 경로만 책임지도록 만든다. 이 구조가 실무에서 Controller, Service 레이어와 맞물리는 방식을 살펴본다.

express.Router란

express.Router는 미니 Express 앱처럼 동작하는 독립 라우터 인스턴스다. 자체 Middleware 스택과 라우트를 가지며, 메인 앱에 app.use로 마운트된다.

app.ts
  └── app.use('/users', userRouter)
        └── userRouter
              ├── GET  /        → list users
              ├── GET  /:id     → get user
              └── POST /        → create user

마운트(mount) — app.use(path, router)로 서브 라우터를 특정 경로에 연결하는 것. 이후 router 내부 경로는 마운트 경로를 prefix로 상속한다.

기본 사용법

// src/routes/users.ts
import { Router } from 'express';

const router = Router();

router.get('/', (req, res) => {
  res.json({ users: [] });
});

router.get('/:id', (req, res) => {
  res.json({ id: req.params.id });
});

router.post('/', (req, res) => {
  res.status(201).json({ created: true });
});

export default router;
// src/app.ts
import express from 'express';
import userRouter from './routes/users';

const app = express();
app.use(express.json());
app.use('/users', userRouter);

export default app;

Router Middleware

Router 인스턴스에 router.use로 해당 라우터 전체에만 적용되는 Middleware를 등록할 수 있다. 인증 같은 횡단 관심사를 라우터 단위로 격리하기 좋다.

import { Router, Request, Response, NextFunction } from 'express';

const router = Router();

// 이 라우터의 모든 경로에 적용
router.use((req: Request, res: Response, next: NextFunction) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
});

router.get('/profile', (req, res) => {
  res.json({ profile: {} });
});

export default router;

Router.param

:id처럼 경로 파라미터가 존재할 때마다 실행할 콜백을 등록한다. 파라미터 유효성 검사나 DB 조회를 중앙화할 때 유용하다.

router.param('id', (req, res, next, id) => {
  if (isNaN(Number(id))) {
    return res.status(400).json({ error: 'id must be a number' });
  }
  next();
});

router.get('/:id', (req, res) => {
  res.json({ id: req.params.id });
});

router.param — 특정 파라미터가 라우트에 등장할 때마다 선행 실행되는 콜백. 같은 파라미터를 쓰는 여러 라우트에 반복 검증 로직을 넣지 않아도 된다.

실무 폴더 구조

도메인별로 라우터를 분리하고 routes/index.ts에서 통합하면 app.ts가 깔끔하게 유지된다.

src/
├── app.ts
├── routes/
│   ├── index.ts       ← 모든 라우터를 통합
│   ├── users.ts
│   ├── posts.ts
│   └── auth.ts
└── controllers/
    ├── users.ts
    └── posts.ts
// src/routes/index.ts
import { Router } from 'express';
import userRouter from './users';
import postRouter from './posts';
import authRouter from './auth';

const router = Router();

router.use('/users', userRouter);
router.use('/posts', postRouter);
router.use('/auth', authRouter);

export default router;
// src/app.ts
import express from 'express';
import router from './routes';

const app = express();
app.use(express.json());
app.use('/api', router);  // 최종 경로: /api/users, /api/posts ...

export default app;
레이어역할
RouterURL 매핑, Middleware 적용
Controller요청 파싱, 응답 직렬화
Service비즈니스 로직
RepositoryDB 접근

라우터는 요청을 올바른 Controller로 전달하는 교통 정리 역할만 맡는다. 비즈니스 로직이 라우터 콜백 안에 직접 들어가기 시작하면 분리가 필요하다는 신호다.

← 이전 글Express Middleware — 요청과 응답 사이에서 동작하는 함수
다음 글 →Express Error Handling — 에러를 중앙에서 처리하는 방법