Skip to content

fix: /mcp rate limiter는 서버리스 환경에서 효과가 없습니다 #922

@jk-kim0

Description

@jk-kim0

문제 설명

src/app/mcp/route.ts의 rate limiter는 module-level Map으로 구현되어 있습니다.

```ts
const rateLimitStore = new Map<string, { count: number; resetAt: number }>();
```

영향

1. 서버리스 cold start 시 초기화

Vercel 등 서버리스 환경에서는 요청마다 새 인스턴스가 뜰 수 있으며, 인스턴스별로 별도의 rateLimitStore를 가집니다. 결과적으로:

  • 동일 IP가 분당 120회 제한을 초과해도 다른 인스턴스에서는 카운트가 0부터 시작
  • 트래픽이 여러 인스턴스에 분산되면 실질적으로 rate limiting이 작동하지 않음

2. 만료 엔트리 누적

현재 구현은 만료된 엔트리(resetAt <= now)를 Map에서 제거하지 않습니다. 장시간 실행 환경(컨테이너, PM2 등)에서는 접속한 IP마다 엔트리가 쌓여 메모리가 지속적으로 증가합니다.

3. x-forwarded-for 부재 시 'unknown' 공유 버킷

프록시 헤더 없이 직접 연결하는 모든 클라이언트가 'unknown'이라는 동일 키를 공유합니다. 한 클라이언트가 rate limit을 소진하면 다른 직접 연결 클라이언트도 차단되고, 반대로 서버리스 환경에서는 이 버킷도 인스턴스별로 분리되어 의미가 없습니다.

개선 방향

  • 단기: 만료 엔트리 주기적 정리로 메모리 누수 방지
  • 중기: Redis / Vercel KV / Upstash 등 외부 KV 스토어 기반 분산 rate limiter로 교체
  • 참고: Upstash Ratelimit — Next.js 서버리스에 최적화된 rate limiting 라이브러리

관련 코드

  • src/app/mcp/route.tsrateLimitStore, isRateLimited, getClientKey

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions