API는 무분별한 요청으로부터 보호해야 합니다. 이 글에서는 Upstash Rate Limit SDK를 활용해 Next.js API 라우트에 속도 제한(rate limiting)을 적용하는 방법을 단계별로 소개합니다.
1. 데이터베이스 설정
Upstash Console 또는 Upstash CLI를 사용하여 Redis 데이터베이스를 생성합니다. 이후 단계에서 필요하므로 UPSTASH_REDIS_REST_URL과 UPSTASH_REDIS_REST_TOKEN 값을 미리 복사해 두세요.
2. 프로젝트 설정
Next.js 애플리케이션을 생성하고 Vercel에 배포하는 것을 전제로 진행합니다.
npx create-next-app@latest
다음으로 속도 제한 패키지를 설치합니다:
npm install @upstash/ratelimit @upstash/redis
3. 코드 작성
pages/api/hello.js 파일을 아래와 같이 수정하고, UPSTASH_REDIS_REST_URL과 UPSTASH_REDIS_REST_TOKEN을 실제 발급받은 값으로 교체하세요.
pages/api/hello.js
import {Ratelimit} from "@upstash/ratelimit";
import {Redis} from "@upstash/redis";
const redis = new Redis({
url: 'UPSTASH_REDIS_REST_URL',
token: 'UPSTASH_REDIS_REST_TOKEN',
})
// 5초 동안 5개의 요청만 허용하는 속도 제한기 생성
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.fixedWindow(5, "5 s"),
});
export default async function handler(req, res) {
// 모든 요청에 하나의 제한을 두려면 고정 문자열 사용
// 또는 userID, apiKey, IP 주소를 사용해 개별 제한 가능
const identifier = "api";
const result = await ratelimit.limit(identifier);
res.setHeader('X-RateLimit-Limit', result.limit)
res.setHeader('X-RateLimit-Remaining', result.remaining)
if (!result.success) {
res.status(200).json({message: '요청 횟수가 제한되었습니다.', rateLimitState: result})
return
}
res.status(200).json({name: 'John Doe', rateLimitState: result})
}
위 코드는 5초 동안 최대 5개의 요청만 허용하도록 설정되어 있습니다.
4. 실행 및 확인
npm run dev로 앱을 실행한 뒤, 브라우저에서 새로고침을 5번 이상 반복해 보세요. 속도 제한이 실제로 동작하는 것을 확인할 수 있습니다.
{"message":"요청 횟수가 제한되었습니다.","rateLimitState":{"success":false,"limit":5,"remaining":-1,"reset":1654546770000,"pending":{}}}
5. 추가 개선 아이디어
- 사용자별 제한 적용: 사용자 ID나 IP 주소를 식별자로 사용하면 사용자마다 개별적인 사용량 제한을 걸 수 있습니다.
const identifier = getClientIp(req);
const result = await ratelimit.limit(identifier);
- 슬라이딩 윈도우(Sliding Window) 알고리즘: 고정 윈도우보다 부드러운 속도 제한 경험을 제공합니다. 다만 비용이 더 많이 들 수 있습니다.
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
- 토큰 버킷(Token Bucket) 알고리즘: 일정 수준의 순간 트래픽 스파이크를 허용하고 싶을 때 유용합니다.
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.tokenBucket(5, "10 s", 10),
});
- 멀티 리전 Redis 활용: Next.js 애플리케이션이 여러 리전에 배포되어 있다면, 각 리전에 Redis 인스턴스를 두어 지연 시간(latency)을 최소화할 수 있습니다.
- 환경 변수로 자격 증명 관리: Upstash Redis 접속 정보는 코드에 하드코딩하지 말고, 환경 변수나 시크릿 스토어(Secret Store)에 안전하게 보관하고 불러오세요.