서버리스(Serverless)의 가장 큰 장점 중 하나는 대규모 트래픽 급증 상황에서도 자동으로 확장할 수 있다는 점입니다. 하지만 안타깝게도 확장은 비용과 기술 양면에서 결코 공짜가 아닙니다. 그렇기 때문에 개발자는 애플리케이션의 확장성을 적절히 통제해야 합니다. 서버리스 애플리케이션에 속도 제한 메커니즘이 필요한 주요 이유를 살펴보겠습니다.
속도 제한이 필요한 이유
1. 리소스 보호
공개 API를 제공하는 경우, 트래픽 급증은 서비스 품질 저하로 이어질 수 있고 최악의 경우 모든 사용자에게 서비스 중단을 초래할 수 있습니다. 연쇄 장애(Cascading Failure)나 내부에서 비롯된 DDoS 사고로부터 시스템을 보호해야 합니다. 애플리케이션의 버그 하나만으로도 이런 문제가 발생할 수 있는데, 예를 들어 실패 시 특정 엔드포인트를 무한히 재시도하는 내부 프로세스는 순식간에 리소스를 고갈시킬 수 있습니다.
2. 사용자 쿼터 관리
서비스의 공정한 사용을 위해 사용자별 사용량 한도(Quota)를 정의해야 할 수 있습니다. 또한 요금제를 여러 단계로 나누어 서비스를 제공하는 경우에도 쿼터 관리가 필수적입니다.
3. 비용 통제
통제되지 않은 시스템이 거액의 청구서를 만들어낸 실제 사례가 많습니다. 높은 확장성 덕분에 서버리스 애플리케이션은 이러한 위험에 더욱 노출되어 있으며, 속도 제한은 이러한 비용을 통제하는 데 큰 도움이 됩니다.
속도 제한 솔루션 3가지
속도 제한은 다양한 계층에서 여러 방식으로 구현할 수 있습니다. 대표적인 3가지 솔루션과 각각의 장단점을 비교해 보겠습니다.
1. 함수 동시성(Concurrency) 수준 제한
클라우드 제공업체는 서버리스 함수 실행을 확장하기 위해 여러 컨테이너를 생성합니다. 동시에 실행될 수 있는 최대 컨테이너·인스턴스 수에 제한을 설정할 수 있습니다. 다만 이 방법은 동시성만 제어할 뿐, 함수가 초당 몇 번 호출되는지는 통제하지 못합니다. AWS Lambda와 Google Cloud Functions의 동시성 제한 설정 방법은 각 클라우드의 공식 문서를 참고하시기 바랍니다.
장점:
- 추가 오버헤드 없음
- 설정이 간편함
단점:
- 완전한 해결책이 아님 — 동시성만 제어하며 초당 실행 횟수는 제한 불가
2. API 게이트웨이에서의 속도 제한
API 게이트웨이를 통해 함수에 접근한다면, 게이트웨이 차원에서 속도 제한 정책을 적용할 수 있습니다. AWS와 GCP 모두 상세한 설정 가이드를 제공합니다.
장점:
- 추가 오버헤드 없음
- 설정이 간편함
단점:
- API 게이트웨이를 사용하는 경우에만 적용 가능
- 사용자별 또는 IP별 쿼터 같은 세밀한 정책은 지원하지 않음
3. Redis를 활용한 속도 제한
가장 완성도 높고 강력한 솔루션입니다. Redis 기반 속도 제한 라이브러리가 다양하게 존재합니다. Jeremy Daly는 블로그 포스트에서 Elasticache를 후보에서 배제하며 "비서버리스(non-serverless) 구성 요소가 추가되고 관리할 대상이 하나 늘어난다"고 지적했습니다. 서버리스 모델과 요청당 과금 방식을 제공하는 Upstash는 이런 상황에서 매우 좋은 대안이 됩니다.
장점:
- 강력함 — 사용자 모델에 맞춘 커스텀 로직 구현 가능
- 확장 가능한 솔루션 — GitHub가 Redis로 속도 제한을 구현한 사례 참고
- 풍부한 생태계 — redis_rate, redis-cell, node-ratelimiter 등 오픈소스 라이브러리 다수
단점:
- Redis 운영에 따른 오버헤드 발생
코드 예제: Redis로 속도 제한 구현하기
속도 제한 라이브러리 덕분에 애플리케이션 코드에 속도 제한을 적용하는 일은 매우 간단합니다. 아래 예제 코드는 AWS Lambda 함수를 IP당 5초에 1회로 제한합니다:
const RateLimiter = require("async-ratelimiter");
const Redis = require("ioredis");
const { getClientIp } = require("request-ip");
const rateLimiter = new RateLimiter({
db: new Redis("YOUR_REDIS_URL"),
max: 1,
duration: 5_000,
});
module.exports.hello = async (event) => {
const clientIp = getClientIp(event) || "NA";
const limit = await rateLimiter.get({ id: clientIp });
if (!limit.remaining) {
return {
statusCode: 429,
body: JSON.stringify({
message: "Sorry, you are rate limited. Wait for 5 seconds",
}),
};
}
return {
statusCode: 200,
body: JSON.stringify({
message: "hello!",
}),
};
};전체 예제는 관련 튜토리얼에서 확인할 수 있습니다.
더 읽을거리
- Google Cloud — 속도 제한 전략 및 기법 (cloud.google.com/architecture/rate-limiting-strategies-techniques)
- Jeremy Daly — AWS Lambda로 서드파티 API 호출 스로틀링 (jeremydaly.com)
- Google Cloud — Cloud Endpoints 쿼터로 API 사용량 제한 (medium.com/google-cloud)
- GitHub Blog — 샤딩·복제된 Redis 속도 제한기로 GitHub API 확장하기 (github.blog)
- Redis 공식 문서 — INCR 명령어 활용 패턴: 속도 제한기 (redis.io)
- Stripe Blog — 속도 제한기 설계 (stripe.com/blog/rate-limiters)