í í° ë²í· ìê³ ëŠ¬ìŠì ì€ì¬ìŒë¡ ìì² ìë ì í ì ëµì íìí©ëë€. ë³µìë ¥ ìê³ íì¥ ê°ë¥í ì í늬ìŒìŽì 구ì¶ì ìí 구í, ì¥ëšì ë° ì€ì ì¬ì© ì¬ë¡ë¥Œ ìì볎ìžì.
ìì² ìë ì í(Rate Limiting): í í° ë²í· 구íì ëí ì¬ìžµ ë¶ì
ì€ëë 곌 ê°ìŽ ìíž ì°ê²°ë ëì§íž í겜ììë ì í늬ìŒìŽì 곌 APIì ìì ì±ê³Œ ê°ì©ì±ì 볎ì¥íë ê²ìŽ ë¬Žìë³Žë€ ì€ìí©ëë€. ìì² ìë ì í(Rate Limiting)ì ì¬ì©ìë íŽëŒìŽìžížê° ìì²ì ë³ŽëŒ ì ìë ìë륌 ì ìŽíšìŒë¡ìš ìŽë¬í 목í륌 ë¬ì±íë ë° ì€ìí ìí ì í©ëë€. ìŽ ëžë¡ê·ž ê²ì묌ììë í í° ë²í·(Token Bucket) ìê³ ëŠ¬ìŠ, ê·ž 구í ë°©ì, ì¥ì 곌 ëšì ì í¹í ì€ì ì ëìŽ ìì² ìë ì í ì ëµì í¬êŽì ìŒë¡ íìí©ëë€.
ìì² ìë ì íìŽë 묎ììžê°?
ìì² ìë ì íì í¹ì êž°ê° ëì ìë²ë ìë¹ì€ë¡ ì ì¡ëë ížëíœì ìì ì ìŽíë ë° ì¬ì©ëë êž°ì ì ëë€. ìŽë 곌ëí ìì²ìŒë¡ ìžíŽ ìì€í ìŽ ìëëë ê²ì ë°©ì§íì¬ ìë¹ì€ ê±°ë¶(DoS) 공격, ì ì© ë° ìêž°ì¹ ìì ížëíœ êžìŠì ë§ìì€ëë€. ìì² ìì ì íì ë ìŒë¡ìš ìì² ìë ì íì ê³µì í ì¬ì©ì 볎ì¥íê³ ì ë°ì ìž ìì€í ì±ë¥ì ê°ì íë©° 볎ìì ê°íí©ëë€.
íëì ìžìŒ ì€ìž ì ììê±°ë íë«íŒì ìê°íŽ ë³Žìžì. ìì² ìë ì íìŽ ìë€ë©Ž ê°ìì€ë¬ìŽ ì¬ì©ì ìì² êžìŠìŒë¡ ìë²ê° 곌ë¶íëìŽ ìëµ ìê°ìŽ ëë €ì§ê±°ë ìë¹ì€ ì€ëšìŽ ë°ìí ì ììµëë€. ìì² ìë ì íì ì¬ì©ìê°(ëë IP 죌ìê°) ì íŽì§ ìê° ëŽì í ì ìë ìì² ì륌 ì ííì¬ ëªšë ì¬ì©ììê² ë ìíí 겜íì 볎ì¥íšìŒë¡ìš ìŽë¥Œ ë°©ì§í ì ììµëë€.
ìì² ìë ì íì ì ì€ìíê°?
ìì² ìë ì íì ë€ì곌 ê°ì ìë§ì ìŽì ì ì ê³µí©ëë€:
- ìë¹ì€ ê±°ë¶(DoS) 공격 ë°©ì§: ëšìŒ ìì€ë¡ë¶í°ì ìì² ìë륌 ì ííšìŒë¡ìš, ìì² ìë ì íì ì ìì ìž ížëíœìŒë¡ ìë²ë¥Œ ìëíë €ë DoS 공격ì ìí¥ì ìíí©ëë€.
- ì ì© ë°©ì§: ìì² ìë ì íì ì ìì ìž íììê° ë°ìŽí° ì€í¬ëíìŽë ê°ì§ ê³ì ìì±ê³Œ ê°ìŽ APIë ìë¹ì€ë¥Œ ëšì©íë ê²ì ìµì í ì ììµëë€.
- ê³µì í ì¬ì© 볎ì¥: ìì² ìë ì íì ê°ë³ ì¬ì©ìë íŽëŒìŽìžížê° 늬ìì€ë¥Œ ë ì íë ê²ì ë°©ì§íê³ ëªšë ì¬ì©ìê° ìë¹ì€ì ê³µííê² ì ê·Œí êž°í륌 ê°ëë¡ ë³Žì¥í©ëë€.
- ìì€í ì±ë¥ í¥ì: ìì² ìë륌 ì ìŽíšìŒë¡ìš, ìì² ìë ì íì ìë²ì 곌ë¶í륌 ë°©ì§íì¬ ë ë¹ ë¥ž ìëµ ìê°ê³Œ í¥ìë ì ë°ì ìž ìì€í ì±ë¥ìŒë¡ ìŽìŽì§ëë€.
- ë¹ì© êŽëЬ: íŽëŒì°ë êž°ë° ìë¹ì€ì 겜ì°, ìì² ìë ì íì ìêž°ì¹ ìì ìêžìŒë¡ ìŽìŽì§ ì ìë 곌ëí ì¬ì©ì ë°©ì§íì¬ ë¹ì©ì íµì íë ë° ëììŽ ë ì ììµëë€.
ìŒë°ì ìž ìì² ìë ì í ìê³ ëŠ¬ìŠ
ìì² ìë ì íì 구ííë ë°ë ì¬ë¬ ìê³ ëŠ¬ìŠì ì¬ì©í ì ììµëë€. ê°ì¥ ìŒë°ì ìž ëª ê°ì§ë ë€ì곌 ê°ìµëë€:
- í í° ë²í· (Token Bucket): ìŽ ìê³ ëŠ¬ìŠì í í°ì ëŽë ê°ë ì ìž "ë²í·"ì ì¬ì©í©ëë€. ê° ìì²ì íëì í í°ì ìë¹í©ëë€. ë²í·ìŽ ë¹ìŽ ììŒë©Ž ìì²ìŽ ê±°ë¶ë©ëë€. í í°ì ì ìë ìëë¡ ë²í·ì ì¶ê°ë©ëë€.
- ëì ë²í· (Leaky Bucket): í í° ë²í·ê³Œ ì ì¬íì§ë§, ìì²ì ëì°© ìëì êŽê³ììŽ ê³ ì ë ìëë¡ ì²ëЬë©ëë€. ìŽê³Œ ìì²ì íì ì ì¥ëê±°ë íêž°ë©ëë€.
- ê³ ì ìëì° ì¹ŽìŽí° (Fixed Window Counter): ìŽ ìê³ ëŠ¬ìŠì ìê°ì ê³ ì ë í¬êž°ì ìëì°ë¡ ëëê³ ê° ìëì° ëŽì ìì² ì륌 ê³ì°í©ëë€. íëì ëë¬í멎 ìëì°ê° ì¬ì€ì ë ëê¹ì§ íì ìì²ìŽ ê±°ë¶ë©ëë€.
- ì¬ëŒìŽë© ìëì° ë¡ê·ž (Sliding Window Log): ìŽ ì ê·Œ ë°©ìì ì¬ëŒìŽë© ìëì° ëŽì ìì² íìì€í¬í ë¡ê·žë¥Œ ì ì§í©ëë€. ìëì° ëŽì ìì² ìë ë¡ê·žë¥Œ êž°ë°ìŒë¡ ê³ì°ë©ëë€.
- ì¬ëŒìŽë© ìëì° ì¹ŽìŽí° (Sliding Window Counter): ê³ ì ìëì°ì ì¬ëŒìŽë© ìëì° ìê³ ëŠ¬ìŠì 잡멎ì ê²°í©íì¬ ì íë륌 í¥ììíš íìŽëžëЬë ì ê·Œ ë°©ìì ëë€.
ìŽ ëžë¡ê·ž ê²ì묌ì ì ì°ì±ê³Œ ëì ì ì© ê°ë¥ì± ë묞ì í í° ë²í· ìê³ ëŠ¬ìŠì ì€ì ì ë ê²ì ëë€.
í í° ë²í· ìê³ ëŠ¬ìŠ: ììž ì€ëª
í í° ë²í· ìê³ ëŠ¬ìŠì ëšìì±ê³Œ íšìšì± ì¬ìŽì ê· íì ì ê³µíë ë늬 ì¬ì©ëë ìì² ìë ì í êž°ì ì ëë€. ìŽë ê°ë ì ìŒë¡ í í°ì ëŽë "ë²í·"ì ì ì§íë ë°©ììŒë¡ ìëí©ëë€. ë€ìŽì€ë ê° ìì²ì ë²í·ìì í í° íë륌 ìë¹í©ëë€. ë²í·ì ì¶©ë¶í í í°ìŽ ììŒë©Ž ìì²ìŽ íì©ëê³ , ê·žë ì§ ììŒë©Ž ìì²ìŽ ê±°ë¶ë©ëë€(ëë 구íì ë°ëŒ íì ì ì¥ëš). í í°ì ì ìë ìëë¡ ë²í·ì ì¶ê°ëìŽ ì¬ì© ê°ë¥í ì©ëì 볎충í©ëë€.
íµì¬ ê°ë
- ë²í· ì©ë(Bucket Capacity): ë²í·ìŽ ëŽì ì ìë ìµë í í° ìì ëë€. ìŽë ë²ì€íž(burst) ì©ëì ê²°ì íë©°, í¹ì ìì ìì²ìŽ ë¹ ë¥ž ì°ììŒë¡ ì²ëЬë ì ìëë¡ íì©í©ëë€.
- 늬í ìë(Refill Rate): í í°ìŽ ë²í·ì ì¶ê°ëë ìëë¡, ìŒë°ì ìŒë¡ ìŽë¹ í í° ì(ëë ë€ë¥ž ìê° ëšì)ë¡ ìž¡ì ë©ëë€. ìŽë ìì²ìŽ ì²ëЬë ì ìë íê· ìë륌 ì ìŽí©ëë€.
- ìì² ìë¹(Request Consumption): ë€ìŽì€ë ê° ìì²ì ë²í·ìì í¹ì ìì í í°ì ìë¹í©ëë€. ìŒë°ì ìŒë¡ ê° ìì²ì íëì í í°ì ìë¹íì§ë§, ë ë³µì¡í ìë늬ì€ììë ë€ìí ì íì ìì²ì ë€ë¥ž í í° ë¹ì©ì í ë¹í ì ììµëë€.
ìë ë°©ì
- ìì²ìŽ ëì°©í멎 ìê³ ëŠ¬ìŠì ë²í·ì ì¶©ë¶í í í°ìŽ ìëì§ íìží©ëë€.
- í í°ìŽ ì¶©ë¶í멎 ìì²ìŽ íì©ëê³ íŽë¹ ìì í í°ìŽ ë²í·ìì ì ê±°ë©ëë€.
- í í°ìŽ ì¶©ë¶íì§ ììŒë©Ž ìì²ì ê±°ë¶ëê±°ë("Too Many Requests" ì€ë¥, ìŒë°ì ìŒë¡ HTTP 429 ë°í) ëì€ì ì²ëЬíêž° ìíŽ íì ì ì¥ë©ëë€.
- ìì² ë착곌 묎êŽíê², í í°ì ì ìë 늬í ìëë¡ ë²í·ì ì©ëê¹ì§ 죌Ʞì ìŒë¡ ì¶ê°ë©ëë€.
ìì
ì©ëìŽ 10ê° í í°ìŽê³ 늬í ìëê° ìŽë¹ 2ê° í í°ìž í í° ë²í·ì ììíŽ ë³Žìžì. ì²ìì ë²í·ì ê°ë ì°š ììµëë€(10ê° í í°). ìê³ ëŠ¬ìŠìŽ ëìíë ë°©ìì ë€ì곌 ê°ìµëë€:
- 0ìŽ: 5ê°ì ìì²ìŽ ëì°©í©ëë€. ë²í·ì í í°ìŽ ì¶©ë¶íë¯ë¡ 5ê° ìì² ëªšë íì©ëê³ , ë²í·ìë ìŽì 5ê°ì í í°ìŽ ììµëë€.
- 1ìŽ: ìì²ìŽ ëì°©íì§ ììµëë€. 2ê°ì í í°ìŽ ë²í·ì ì¶ê°ëìŽ ìŽ 7ê°ì í í°ìŽ ë©ëë€.
- 2ìŽ: 4ê°ì ìì²ìŽ ëì°©í©ëë€. ë²í·ì í í°ìŽ ì¶©ë¶íë¯ë¡ 4ê° ìì² ëªšë íì©ëê³ , ë²í·ìë ìŽì 3ê°ì í í°ìŽ ëšìµëë€. 2ê°ì í í°ë ì¶ê°ëìŽ ìŽ 5ê°ì í í°ìŽ ë©ëë€.
- 3ìŽ: 8ê°ì ìì²ìŽ ëì°©í©ëë€. 5ê°ì ìì²ë§ íì©ë ì ììŒë©°(ë²í·ì 5ê° í í° ìì), ëëšžì§ 3ê° ìì²ì ê±°ë¶ëê±°ë íì ì ì¥ë©ëë€. 2ê°ì í í°ë ì¶ê°ëìŽ ìŽ 2ê°ì í í°ìŽ ë©ëë€(5ê° ìì²ìŽ ëŠ¬í 죌Ʞ ì ì ì²ëЬëìë€ë©Ž, ëë 늬íìŽ ìì² ì²ëЬ ì ì ë°ìíë€ë©Ž 7ê°).
í í° ë²í· ìê³ ëŠ¬ìŠ êµ¬ííêž°
í í° ë²í· ìê³ ëŠ¬ìŠì ë€ìí íë¡ê·žëë° ìžìŽë¡ 구íí ì ììµëë€. ë€ìì Golang, Python, Javaì ììì ëë€:
Golang
```go package main import ( "fmt" "sync" "time" ) // TokenBucketì í í° ë²í· ìì² ìë ì íꞰ륌 ëíë ëë€. type TokenBucket struct { capacity int tokens int rate time.Duration lastRefill time.Time mu sync.Mutex } // NewTokenBucketì ìë¡ìŽ TokenBucketì ìì±í©ëë€. func NewTokenBucket(capacity int, rate time.Duration) *TokenBucket { return &TokenBucket{ capacity: capacity, tokens: capacity, rate: rate, lastRefill: time.Now(), } } // Allowë í í° ê°ì©ì±ì ë°ëŒ ìì²ìŽ íì©ëëì§ íìží©ëë€. func (tb *TokenBucket) Allow() bool { tb.mu.Lock() defer tb.mu.Unlock() now := time.Now() tb.refill(now) if tb.tokens > 0 { tb.tokens-- return true } return false } // refillì 겜곌 ìê°ì ë°ëŒ ë²í·ì í í°ì ì¶ê°í©ëë€. func (tb *TokenBucket) refill(now time.Time) { elapsed := now.Sub(tb.lastRefill) newTokens := int(elapsed.Seconds() * float64(tb.capacity) / tb.rate.Seconds()) if newTokens > 0 { tb.tokens += newTokens if tb.tokens > tb.capacity { tb.tokens = tb.capacity } tb.lastRefill = now } } func main() { bucket := NewTokenBucket(10, time.Second) for i := 0; i < 15; i++ { if bucket.Allow() { fmt.Printf("ìì² %d íì©ëš\n", i+1) } else { fmt.Printf("ìì² %d ìë ì íëš\n", i+1) } time.Sleep(100 * time.Millisecond) } } ```
Python
```python import time import threading class TokenBucket: def __init__(self, capacity, refill_rate): self.capacity = capacity self.tokens = capacity self.refill_rate = refill_rate self.last_refill = time.time() self.lock = threading.Lock() def allow(self): with self.lock: self._refill() if self.tokens > 0: self.tokens -= 1 return True return False def _refill(self): now = time.time() elapsed = now - self.last_refill new_tokens = elapsed * self.refill_rate self.tokens = min(self.capacity, self.tokens + new_tokens) self.last_refill = now if __name__ == '__main__': bucket = TokenBucket(capacity=10, refill_rate=2) # 10ê° í í°, ìŽë¹ 2ê° ëŠ¬í for i in range(15): if bucket.allow(): print(f"ìì² {i+1} íì©ëš") else: print(f"ìì² {i+1} ìë ì íëš") time.sleep(0.1) ```
Java
```java import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.TimeUnit; public class TokenBucket { private final int capacity; private double tokens; private final double refillRate; private long lastRefillTimestamp; private final ReentrantLock lock = new ReentrantLock(); public TokenBucket(int capacity, double refillRate) { this.capacity = capacity; this.tokens = capacity; this.refillRate = refillRate; this.lastRefillTimestamp = System.nanoTime(); } public boolean allow() { try { lock.lock(); refill(); if (tokens >= 1) { tokens -= 1; return true; } else { return false; } } finally { lock.unlock(); } } private void refill() { long now = System.nanoTime(); double elapsedTimeInSeconds = (double) (now - lastRefillTimestamp) / TimeUnit.NANOSECONDS.toNanos(1); double newTokens = elapsedTimeInSeconds * refillRate; tokens = Math.min(capacity, tokens + newTokens); lastRefillTimestamp = now; } public static void main(String[] args) throws InterruptedException { TokenBucket bucket = new TokenBucket(10, 2); // 10ê° í í°, ìŽë¹ 2ê° ëŠ¬í for (int i = 0; i < 15; i++) { if (bucket.allow()) { System.out.println("ìì² " + (i + 1) + " íì©ëš"); } else { System.out.println("ìì² " + (i + 1) + " ìë ì íëš"); } TimeUnit.MILLISECONDS.sleep(100); } } } ```
í í° ë²í· ìê³ ëŠ¬ìŠì ì¥ì
- ì ì°ì±: í í° ë²í· ìê³ ëŠ¬ìŠì ë§€ì° ì ì°íë©° ë€ìí ìì² ìë ì í ìë늬ì€ì ìœê² ì ìí ì ììµëë€. ë²í· ì©ë곌 늬í ìë륌 ì¡°ì íì¬ ìì² ìë ì í ëìì ë¯žìž ì¡°ì í ì ììµëë€.
- ë²ì€íž(Burst) ì²ëЬ: ë²í· ì©ëì í¹ì ìì ë²ì€íž ížëíœìŽ ìë ì í ììŽ ì²ëЬë ì ìëë¡ íì©í©ëë€. ìŽë ê°íì ìž ížëíœ êžìŠì ì²ëЬíë ë° ì ì©í©ëë€.
- ëšìì±: ìê³ ëŠ¬ìŠì ë¹êµì ìŽíŽíê³ êµ¬ííêž° ìœìµëë€.
- êµ¬ì± ì©ìŽì±: íê· ìì² ìëì ë²ì€íž ì©ëì ì ë°íê² ì ìŽí ì ììµëë€.
í í° ë²í· ìê³ ëŠ¬ìŠì ëšì
- ë³µì¡ì±: ê°ë ì ëšìíì§ë§, ë²í· ìíì 늬í íë¡ìžì€ë¥Œ êŽëЬíë €ë©Ž í¹í ë¶ì° ìì€í ìì ì ì€í 구íìŽ íìí©ëë€.
- ë¶ê· ë±í ë¶ë°° ê°ë¥ì±: ìŒë¶ ìë늬ì€ììë ë²ì€íž ì©ëìŽ ìê° ê²œê³Œì ë°ë¥ž ìì²ì ë¶ê· ë±í ë¶ë°°ë¡ ìŽìŽì§ ì ììµëë€.
- êµ¬ì± ì€ë²í€ë: ìµì ì ë²í· ì©ë곌 늬í ìë륌 ê²°ì íë ë° ì ì€í ë¶ì곌 ì€íìŽ íìí ì ììµëë€.
í í° ë²í· ìê³ ëŠ¬ìŠì ì¬ì© ì¬ë¡
í í° ë²í· ìê³ ëŠ¬ìŠì ë€ìì í¬íší êŽë²ìí ìì² ìë ì í ì¬ì© ì¬ë¡ì ì í©í©ëë€:
- API ìì² ìë ì í: ì¬ì©ì ëë íŽëŒìŽìžížë¹ ìì² ì륌 ì ííì¬ API륌 ëšì©ìŒë¡ë¶í° 볎ížíê³ ê³µì í ì¬ì©ì 볎ì¥í©ëë€. ì륌 ë€ìŽ, ìì 믞ëìŽ APIë ì€ížì ë°©ì§íêž° ìíŽ ì¬ì©ìê° ìê°ë¹ ì¬ëŠŽ ì ìë ê²ì묌 ì륌 ì íí ì ììµëë€.
- ì¹ ì í늬ìŒìŽì ìì² ìë ì í: ì¬ì©ìê° ìì ì ì¶ìŽë 늬ìì€ ì¡ìžì€ì ê°ìŽ ì¹ ìë²ì 곌ëí ìì²ì íë ê²ì ë°©ì§í©ëë€. ìšëŒìž ë± í¹ ì í늬ìŒìŽì ì ë¬Žì°šë³ ëì 공격ì ë°©ì§íêž° ìíŽ ë¹ë°ë²íž ì¬ì€ì ìë íì륌 ì íí ì ììµëë€.
- ë€ížìí¬ ìì² ìë ì í: í¹ì ì í늬ìŒìŽì ìŽë ì¬ì©ìê° ì¬ì©íë ëìíì ì ííë ë± ë€ížìí¬ë¥Œ íµê³Œíë ížëíœ ìë륌 ì ìŽí©ëë€. ISPë ì¢ ì¢ ë€ížìí¬ íŒì¡ì êŽëЬíêž° ìíŽ ìë ì íì ì¬ì©í©ëë€.
- ë©ìì§ í ìì² ìë ì í: ë©ìì§ íìì ì²ëЬëë ë©ìì§ ìë륌 ì ìŽíì¬ ì»šìëšžê° ê³Œë¶íëë ê²ì ë°©ì§í©ëë€. ìŽë ìë¹ì€ê° ë©ìì§ í륌 íµíŽ ë¹ëêž°ì ìŒë¡ íµì íë ë§ìŽí¬ë¡ìë¹ì€ ìí€í ì²ìì ìŒë°ì ì ëë€.
- ë§ìŽí¬ë¡ìë¹ì€ ìì² ìë ì í: ë€ë¥ž ìë¹ì€ë ìžë¶ íŽëŒìŽìžížë¡ë¶í° ë°ë ìì² ì륌 ì ííì¬ ê°ë³ ë§ìŽí¬ë¡ìë¹ì€ë¥Œ 곌ë¶íë¡ë¶í° 볎íží©ëë€.
ë¶ì° ìì€í ìì í í° ë²í· 구ííêž°
ë¶ì° ìì€í ìì í í° ë²í· ìê³ ëŠ¬ìŠì 구ííë €ë©Ž ìŒêŽì±ì 볎ì¥íê³ ê²œì 조걎(race condition)ì íŒíêž° ìí í¹ë³í ê³ ë € ì¬íìŽ íìí©ëë€. ëª ê°ì§ ìŒë°ì ìž ì ê·Œ ë°©ìì ë€ì곌 ê°ìµëë€:
- ì€ì ì§ì€ì í í° ë²í·: ëšìŒì ì€ì ì§ì€ì ìë¹ì€ê° 몚ë ì¬ì©ì ëë íŽëŒìŽìžížì ëí í í° ë²í·ì êŽëЬí©ëë€. ìŽ ì ê·Œ ë°©ìì 구íìŽ ê°ëšíì§ë§ ë³ëª© íì곌 ëšìŒ ì¥ì ì (single point of failure)ìŽ ë ì ììµëë€.
- Redis륌 ìŽì©í ë¶ì° í í° ë²í·: ìžë©ëªšëЬ ë°ìŽí° ì ì¥ììž Redis륌 ì¬ì©íì¬ í í° ë²í·ì ì ì¥íê³ êŽëЬí ì ììµëë€. Redisë ëìì± í겜ìì ë²í· ìí륌 ìì íê² ì ë°ìŽížíë ë° ì¬ì©í ì ìë ììì (atomic) ì°ì°ì ì ê³µí©ëë€.
- íŽëŒìŽìžíž ìž¡ í í° ë²í·: ê° íŽëŒìŽìžížê° ì첎 í í° ë²í·ì ì ì§í©ëë€. ìŽ ì ê·Œ ë°©ìì íì¥ì±ìŽ ë§€ì° ëì§ë§, ìë ì íì ëí ì€ì íµì ê° ììŒë¯ë¡ ì íëê° ëšìŽì§ ì ììµëë€.
- íìŽëžëЬë ì ê·Œ ë°©ì: ì€ì ì§ì€ì곌 ë¶ì° ë°©ìì 잡멎ì ê²°í©í©ëë€. ì륌 ë€ìŽ, ë¶ì° ìºì륌 ì¬ì©íì¬ í í° ë²í·ì ì ì¥íê³ , ì€ì ì§ì€ì ìë¹ì€ê° ë²í·ì 늬ííë ì± ìì ë§¡ì ì ììµëë€.
Redis ì¬ì© ìì (ê°ë ì )
ë¶ì° í í° ë²í·ì Redis륌 ì¬ì©íë ê²ì í í° ì륌 êŽëЬíêž° ìíŽ ììì ì°ì°(`INCRBY`, `DECR`, `TTL`, `EXPIRE` ë±)ì íì©íë ê²ì í¬íší©ëë€. Ʞ볞 íëŠì ë€ì곌 ê°ìµëë€:
- Ʞ졎 ë²í· íìž: ì¬ì©ì/API ìëí¬ìžížì ëí í€ê° Redisì 졎ì¬íëì§ íìží©ëë€.
- íìì ìì±: ìë€ë©Ž í€ë¥Œ ìì±íê³ , í í° ì륌 ì©ëìŒë¡ ìŽêž°ííë©°, 늬í êž°ê°ê³Œ ìŒì¹íëë¡ ë§ë£ ìê°(TTL)ì ì€ì í©ëë€.
- í í° ìë¹ ìë: ììì ìŒë¡ í í° ì륌 ê°ììíµëë€. ê²°ê³Œê° >= 0ìŽë©Ž ìì²ìŽ íì©ë©ëë€.
- í í° ê³ ê° ì²ëЬ: ê²°ê³Œê° < 0ìŽë©Ž ê°ì륌 ëëëŠ¬ê³ (ììì ìŒë¡ ë€ì ìŠê°ìíŽ) ìì²ì ê±°ë¶í©ëë€.
- 늬í ë¡ì§: 백귞ëŒìŽë íë¡ìžì€ë 죌Ʞì ìž ìì ìŽ ë²í·ì 늬ííì¬ ì©ëê¹ì§ í í°ì ì¶ê°í ì ììµëë€.
ë¶ì° 구íì ìí ì€ì ê³ ë € ì¬í:
- ììì±: ëìì± í겜ìì í í° ìê° ì ííê² ì ë°ìŽížëëë¡ ììì ì°ì°ì ì¬ì©í©ëë€.
- ìŒêŽì±: ë¶ì° ìì€í ì 몚ë ë žëìì í í° ìê° ìŒêŽëëë¡ ë³Žì¥í©ëë€.
- ëŽê²°íšì±: ìŒë¶ ë žëê° ì€íšíëëŒë ìì€í ìŽ ê³ì ìëí ì ìëë¡ ëŽê²°íšì± ìê² ì€ê³í©ëë€.
- íì¥ì±: ì룚ì ì ë§ì ìì ì¬ì©ìì ìì²ì ì²ëЬí ì ìëë¡ íì¥ ê°ë¥íŽìŒ í©ëë€.
- 몚ëí°ë§: ìë ì íì íšê³Œë¥Œ ì¶ì íê³ ë¬žì 륌 ìë³íêž° ìíŽ ëªšëí°ë§ì 구íí©ëë€.
í í° ë²í·ì ëì
í í° ë²í· ìê³ ëŠ¬ìŠìŽ ìžêž° ìë ì íìŽì§ë§, í¹ì ì구 ì¬íì ë°ëŒ ë€ë¥ž ìë ì í êž°ì ìŽ ë ì í©í ì ììµëë€. ëª ê°ì§ ëì곌ì ë¹êµë ë€ì곌 ê°ìµëë€:
- ëì ë²í· (Leaky Bucket): í í° ë²í·ë³Žë€ ê°ëší©ëë€. ê³ ì ë ìëë¡ ìì²ì ì²ëЬí©ëë€. ížëíœì ííííë ë° ì¢ì§ë§ ë²ì€íž ì²ëЬììë í í° ë²í·ë³Žë€ ì ì°ì±ìŽ ëšìŽì§ëë€.
- ê³ ì ìëì° ì¹ŽìŽí° (Fixed Window Counter): 구ííêž° ìœì§ë§, ìëì° ê²œê³ìì ìë ì íì ë 배륌 íì©í ì ììµëë€. í í° ë²í·ë³Žë€ ë ì ë°í©ëë€.
- ì¬ëŒìŽë© ìëì° ë¡ê·ž (Sliding Window Log): ì ííì§ë§, 몚ë ìì²ì êž°ë¡íë¯ë¡ ë©ëªšëЬ ìëªšê° ë íœëë€. ì íì±ìŽ ê°ì¥ ì€ìí ìë늬ì€ì ì í©í©ëë€.
- ì¬ëŒìŽë© ìëì° ì¹ŽìŽí° (Sliding Window Counter): ì íì±ê³Œ ë©ëªšëЬ ì¬ì©ë ì¬ìŽì ì ì¶©ìì ëë€. ì¬ëŒìŽë© ìëì° ë¡ê·žë³Žë€ ì ì ë©ëªšëЬ ì€ë²í€ëë¡ ê³ ì ìëì° ì¹ŽìŽí°ë³Žë€ ëì ì íì±ì ì ê³µí©ëë€.
ì¬ë°ë¥ž ìê³ ëŠ¬ìŠ ì ííêž°:
ìµì ì ìë ì í ìê³ ëŠ¬ìŠ ì íì ë€ì곌 ê°ì ììì ë°ëŒ ë¬ëŒì§ëë€:
- ì íë ì구 ì¬í: ìë ì íì ìŒë§ë ì ë°íê² ìííŽìŒ íëê°?
- ë²ì€íž ì²ëЬ íìì±: ì§§ì ížëíœ ë²ì€ížë¥Œ íì©í íìê° ìëê°?
- ë©ëªšëЬ ì ìœ: ìë ì í ë°ìŽí°ë¥Œ ì ì¥íë ë° ìŒë§ë ë§ì ë©ëªšëŠ¬ë¥Œ í ë¹í ì ìëê°?
- 구í ë³µì¡ì±: ìê³ ëŠ¬ìŠì 구ííê³ ì ì§ êŽëЬíêž°ê° ìŒë§ë ì¬ìŽê°?
- íì¥ì± ì구 ì¬í: ìê³ ëŠ¬ìŠìŽ ë§ì ìì ì¬ì©ìì ìì²ì ì²ëЬíêž° ìíŽ ìŒë§ë ì íì¥ëëê°?
ìì² ìë ì íì ìí ëªšë² ì¬ë¡
ìì² ìë ì íì íšê³Œì ìŒë¡ 구ííë €ë©Ž ì ì€í ê³í곌 ê³ ë €ê° íìí©ëë€. ë€ìì ë°ëŒìŒ í ëª ê°ì§ ëªšë² ì¬ë¡ì ëë€:
- ìë ì í ëª íí ì ìíêž°: ìë² ì©ë, ìì ížëíœ íšíŽ, ì¬ì©ì ì구ì ë°ëŒ ì ì í ìë ì íì ê²°ì í©ëë€.
- ëª íí ì€ë¥ ë©ìì§ ì ê³µíêž°: ìì²ìŽ ìë ì íë멎 ì¬ì©ììê² ìë ì íì ìŽì ì ë€ì ìëí ì ìë ìì (ì: `Retry-After` HTTP í€ë ì¬ì©)ì í¬íší ëª ííê³ ì ìµí ì€ë¥ ë©ìì§ë¥Œ ë°íí©ëë€.
- íì€ HTTP ìí ìœë ì¬ì©íêž°: 429(Too Many Requests)ì ê°ìŽ ìë ì íì ëíëŽë ì ì í HTTP ìí ìœë륌 ì¬ì©í©ëë€.
- ì ì§ì ì±ë¥ ì í 구ííêž°: ìì²ì ëšìí ê±°ë¶íë ëì , ìë¹ì€ íì§ì ë®ì¶ê±°ë ì²ëŠ¬ë¥Œ ì§ì°íë ë± ì ì§ì ì±ë¥ ì í륌 구ííë ê²ì ê³ ë €í©ëë€.
- ìë ì í ë©ížëŠ ëªšëí°ë§íêž°: ìë ì íë ìì² ì, íê· ìëµ ìê° ë° êž°í êŽë š ë©ížëŠì ì¶ì íì¬ ìë ì íìŽ íšê³Œì ìŽê³ ìëíì§ ìì 결곌륌 ìŽëíì§ ìëì§ íìží©ëë€.
- ìë ì íì êµ¬ì± ê°ë¥íê² ë§ë€êž°: êŽëЬìê° ë³ííë ížëíœ íšíŽê³Œ ìì€í ì©ëì ë°ëŒ ëì ìŒë¡ ìë ì íì ì¡°ì í ì ìëë¡ í©ëë€.
- ìë ì í 묞ìííêž°: ê°ë°ìê° ì í ì¬íì ìžì§íê³ ê·žì ë§ê² ì í늬ìŒìŽì ì ì€ê³í ì ìëë¡ API 묞ìì ìë ì íì ëª ííê² ë¬žìíí©ëë€.
- ì ìí ìë ì í ì¬ì©íêž°: íì¬ ìì€í ë¶íì ížëíœ íšíŽì ë°ëŒ ìë ì íì ìëìŒë¡ ì¡°ì íë ì ìí ìë ì í ì¬ì©ì ê³ ë €í©ëë€.
- ìë ì í ì°šë³ííêž°: ë€ë¥ž ì íì ì¬ì©ìë íŽëŒìŽìžížì ë€ë¥ž ìë ì íì ì ì©í©ëë€. ì륌 ë€ìŽ, ìžìŠë ì¬ì©ìë ìµëª ì¬ì©ìë³Žë€ ë ëì ìë ì íì ê°ì§ ì ììµëë€. ë§ì°¬ê°ì§ë¡, ë€ë¥ž API ìëí¬ìžížë ë€ë¥ž ìë ì íì ê°ì§ ì ììµëë€.
- ì§ìì ì°šìŽ ê³ ë €íêž°: ë€ížìí¬ ì¡°ê±Žê³Œ ì¬ì©ì íëìŽ ì§ëЬì ì§ìì ë°ëŒ ë€ë¥Œ ì ììì ìžì§í©ëë€. íìí ê²œì° ê·žì ë§ê² ìë ì íì ì¡°ì í©ëë€.
ê²°ë¡
ìì² ìë ì íì ë³µìë ¥ ìê³ íì¥ ê°ë¥í ì í늬ìŒìŽì ì 구ì¶íêž° ìí íì êž°ì ì ëë€. í í° ë²í· ìê³ ëŠ¬ìŠì ì¬ì©ìë íŽëŒìŽìžížê° ìì²ì ë³ŽëŒ ì ìë ìë륌 ì ìŽíë ì ì°íê³ íšê³Œì ìž ë°©ë²ì ì ê³µíì¬ ìì€í ì ëšì©ìŒë¡ë¶í° 볎ížíê³ ê³µì í ì¬ì©ì 볎ì¥íë©° ì ë°ì ìž ì±ë¥ì í¥ììíµëë€. í í° ë²í· ìê³ ëŠ¬ìŠì ì늬륌 ìŽíŽíê³ êµ¬íì ìí ëªšë² ì¬ë¡ë¥Œ ë°ë¥Žë©Ž, ê°ë°ìë ê°ì¥ ê¹ë€ë¡ìŽ ížëíœ ë¶íë ì²ëЬí ì ìë ê²¬ê³ íê³ ì 뢰í ì ìë ìì€í ì 구ì¶í ì ììµëë€.
ìŽ ëžë¡ê·ž ê²ì묌ì í í° ë²í· ìê³ ëŠ¬ìŠ, ê·ž 구í, ì¥ëšì ë° ì¬ì© ì¬ë¡ì ëí í¬êŽì ìž ê°ì륌 ì ê³µíìµëë€. ìŽ ì§ìì íì©íì¬ ìì ì ì í늬ìŒìŽì ì ìì² ìë ì íì íšê³Œì ìŒë¡ 구ííê³ ì ìžê³ ì¬ì©ì륌 ìíŽ ìë¹ì€ì ìì ì±ê³Œ ê°ì©ì±ì 볎ì¥í ì ììµëë€.