diff options
Diffstat (limited to 'lib/rate-limit.ts')
| -rw-r--r-- | lib/rate-limit.ts | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/lib/rate-limit.ts b/lib/rate-limit.ts new file mode 100644 index 00000000..db90dcff --- /dev/null +++ b/lib/rate-limit.ts @@ -0,0 +1,63 @@ +// lib/rate-limit.ts +import { NextRequest } from 'next/server'; +import { getClientIp } from './network/get-client-ip'; + +interface RateLimitResult { + success: boolean; + remaining?: number; +} + +// 메모리 기반 간단한 rate limiter (프로덕션에서는 Redis 사용 권장) +const requestCounts = new Map<string, { count: number; resetTime: number }>(); + + + +export default async function rateLimit( + request: NextRequest, + maxRequests: number = 100, + windowMs: number = 60 * 60 * 1000 // 1시간 +): Promise<RateLimitResult> { + // 클라이언트 IP 가져오기 + const clientIP = getClientIp(request); + + + const now = Date.now(); + const key = `rate_limit:${clientIP}`; + + // 기존 요청 정보 가져오기 + let requestInfo = requestCounts.get(key); + + // 윈도우가 리셋된 경우 또는 첫 요청인 경우 + if (!requestInfo || now > requestInfo.resetTime) { + requestInfo = { + count: 1, + resetTime: now + windowMs + }; + requestCounts.set(key, requestInfo); + return { success: true, remaining: maxRequests - 1 }; + } + + // 요청 한도 초과 확인 + if (requestInfo.count >= maxRequests) { + return { success: false, remaining: 0 }; + } + + // 요청 카운트 증가 + requestInfo.count++; + requestCounts.set(key, requestInfo); + + return { + success: true, + remaining: maxRequests - requestInfo.count + }; +} + +// 주기적으로 만료된 항목 정리 (메모리 누수 방지) +setInterval(() => { + const now = Date.now(); + for (const [key, value] of requestCounts.entries()) { + if (now > value.resetTime) { + requestCounts.delete(key); + } + } +}, 60 * 60 * 1000); // 1시간마다 정리
\ No newline at end of file |
