codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›backend

API 핸들러 패턴

2026-04-28 게시· 2026-05-18 갱신·0회 조회

API 핸들러 패턴과 횡단 관심사

HTTP API 를 짜다 보면 핸들러 본체보다 그 주변 (인증·검증·로깅·에러 변환·rate limit) 코드가 더 많아질 때가 있습니다. 이 횡단 관심사를 어디에 어떻게 두느냐가 일관성과 유지비를 가릅니다.

1. 프레임워크별 메커니즘

횡단 관심사를 표현하는 메커니즘은 프레임워크마다 이름이 다릅니다.

프레임워크 메커니즘 형태
Express (Node) middleware (req, res, next) => ...
Koa middleware async (ctx, next) => ...
Spring MVC HandlerInterceptor · Filter 클래스 + 어노테이션
FastAPI Depends · middleware 함수 의존 + ASGI middleware
NestJS Interceptor · Guard · Pipe 데코레이터 + 클래스
Next.js Route Handlers wrapper 함수 withAuth(handler) 같은 합성
Hono middleware app.use(...)

표준 응답 형식 중 하나는 RFC 7807 — Problem Details for HTTP APIs (2016, 후에 RFC 9457 로 개정) 입니다. application/problem+json 콘텐츠 타입과 type · title · status · detail · instance 필드를 정의합니다.

2. 미들웨어 체인 (Express 모델)

요청은 등록 순서대로 미들웨어를 통과하고 각 미들웨어가 next() 를 호출하면 다음으로 넘어갑니다. 응답을 직접 보내거나 에러를 던지면 거기서 체인이 끊깁니다.

app.use(requestId)
app.use(logger)
app.use(authenticate)
app.use('/admin', authorize('admin'))
app.use(routes)
app.use(errorHandler)  // (err, req, res, next) — 에러 전용

Koa 는 같은 모델을 async/await 로 다시 표현했습니다. "양파 모델" 이라고 부르는 이유는 응답 후에도 같은 미들웨어가 깨어나 마무리 처리를 할 수 있기 때문입니다.

3. Spring 의 Interceptor · Filter

Servlet Filter 가 가장 바깥, HandlerInterceptor 가 컨트롤러 가까이에서 동작합니다. @RestControllerAdvice 는 예외를 한 곳에서 변환합니다.

@RestControllerAdvice
class GlobalExceptionHandler {
  @ExceptionHandler(NotFoundException.class)
  ResponseEntity<ProblemDetail> notFound(NotFoundException e) {
    var p = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
    return ResponseEntity.of(p).build();
  }
}

Spring 6 부터 ProblemDetail 클래스가 RFC 7807 응답을 표준화해서 만듭니다.

4. FastAPI 의 Depends + middleware

Depends 는 핸들러 진입 직전에 평가되며 인증·DB 세션·검증을 담습니다. app.add_middleware 는 ASGI 레벨의 전역 처리를 담습니다. FastAPI 는 HTTPException 외에 사용자 정의 예외를 app.exception_handler 로 변환할 수 있습니다.

5. Next.js wrapper

Next.js App Router 의 route.ts 는 함수입니다. 합성으로 횡단 관심사를 더합니다.

export const GET = withAuth(withLogging(async (req) => {
  return NextResponse.json({ ok: true })
}))

함수 합성은 데코레이터·클래스를 쓰지 않아 가볍지만 순서·타입을 손으로 관리해야 합니다.

6. RFC 7807 응답

{
  "type": "https://example.com/probs/out-of-credit",
  "title": "You do not have enough credit.",
  "status": 403,
  "detail": "Your current balance is 30, but that costs 50.",
  "instance": "/account/12345/msgs/abc"
}

API 클라이언트가 응답을 파싱할 때 표준 필드를 가정할 수 있다는 것이 강점입니다. errors[] 같은 자체 필드를 더하는 것도 사양상 허용됩니다.

7. 함수 합성 vs 클래스 데코레이터

  • 함수 합성 (Express · Hono · Next wrapper) — 흐름이 위에서 아래로 보입니다. 의존이 명시적입니다. 깊어지면 들여쓰기가 길어집니다.
  • 클래스 데코레이터 (NestJS · Spring) — 메타데이터로 선언합니다. 핸들러가 짧아집니다. 어노테이션의 적용 순서·우선순위가 암묵적일 수 있습니다.
  • dependency 주입 (FastAPI Depends) — 시그니처에 의존이 적힙니다. 합성과 데코레이터의 중간 형태입니다.

어느 쪽이든 횡단 관심사가 한 곳에 모이고 핸들러는 본체에 집중한다는 목표는 같습니다.

8. ApiError 표준 응답 클래스

자체 ApiError 클래스를 두는 패턴은 흔합니다.

class ApiError extends Error {
  constructor(public status: number, public code: string, public detail?: string) { super(detail) }
}

throw 하면 전역 핸들러가 problem+json 으로 변환합니다. RFC 7807 의 type 을 도메인 코드와 매핑해 두면 클라이언트가 분기할 수 있습니다.

9. 미들웨어 순서

대체로 다음 순서가 무난합니다.

① 요청 ID 부여 (관측성)
② 보안 헤더 (Helmet 같은 것)
③ CORS
④ 로깅
⑤ body 파싱
⑥ 인증
⑦ 인가 (라우트별)
⑧ 검증 (스키마)
⑨ 라우트 핸들러
⑩ 에러 핸들러 (가장 마지막)

Rate limit 은 미들웨어 자리입니다. Redis 기반의 토큰 버킷·sliding window 가 흔합니다 (express-rate-limit · @upstash/ratelimit · Spring 의 Bucket4j).

10. 자주 걸리는 자리

에러 미들웨어가 비동기 throw 를 못 잡는 경우 — Express 4 는 async 핸들러의 reject 를 자동 전파하지 않습니다. express-async-errors 같은 보조나 try/catch wrapper 가 필요합니다. Express 5 에서 개선됐습니다.

스택 너머의 응답 종료 — 한 미들웨어가 응답을 보낸 뒤 다음 미들웨어가 또 보내면 ERR_HTTP_HEADERS_SENT 가 납니다. next() 호출 시점·return 을 정리합니다.

클라이언트 IP 와 프록시 — Caddy/nginx 뒤에 있을 때 X-Forwarded-For 신뢰 설정이 빠지면 rate limit 이 프록시 IP 한 개로 묶입니다.

에러 메시지의 정보 노출 — detail 에 스택 트레이스·DB 메시지가 그대로 들어가지 않도록 운영 환경에서 마스킹합니다.

하고픈 말

핸들러 본체보다 횡단 관심사가 코드의 절반이 되는 자리에서는 wrapper 또는 미들웨어로 묶는 가치가 분명히 보입니다. 처음에는 직접 try/catch 로 시작해 패턴이 반복될 때 추상으로 옮겨가는 흐름이 가장 안전합니다.

Next

  • jobs-apscheduler
  • typeorm-readonly

RFC 7807 · RFC 9457 (개정) · Express middleware · Spring RestControllerAdvice · FastAPI Dependencies · NestJS Interceptors · Hono Middleware 를 참고합니다.

backend 카테고리의 다른 글

카테고리 전체 보기 →
  • 공공 OpenAPI 는 자체 BFF 로 한 번 감싼다
  • 이메일 발송과 OTP — SMTP
  • 감사로그 — logAdminAction 패턴
  • WebSocket · SSE — 실시간 통신
  • REST API 입문
  • OpenAPI 사양