๐Ÿš€ ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์—์„œ Spring AOP vs Filter, ๋‹น์‹ ์˜ ์„ ํƒ์€?

jkky98ยท2025๋…„ 2์›” 15์ผ
0

ProjectSpring

๋ชฉ๋ก ๋ณด๊ธฐ
12/20

๊ธฐ์กด ์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜ ๊ตฌํ˜„ ๋ณ€๊ฒฝ ์ด์œ 

๊ธฐ์กด ์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์— Spring AOP ์• ๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ ๊ณผ๋„ํ•œ ์š”์ฒญ์„ ์ œํ•œํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค.

๐Ÿ“– ใ€Š๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„ ๊ธฐ์ดˆใ€‹ 4์žฅ์—์„œ ๋‹ค๋ฃฌ ์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜ ์„ค๊ณ„๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ, ๋ณด๋‹ค ํšจ์œจ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ•œ๋‹ค.

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋Š” ๋Œ€๊ทœ๋ชจ ํ™˜๊ฒฝ์€ ์•„๋‹ˆ์ง€๋งŒ, ํ•ด๋‹น ์ฑ•ํ„ฐ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์บ์‹œ ํ™œ์šฉ๋ฒ•, ์ œํ•œ ์žฅ์น˜์˜ ์œ„์น˜ ์„ ์ •์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ํ•™์Šตํ•˜์˜€๊ณ , ์ด๋ฅผ ์ ์šฉํ•˜์—ฌ ๊ธฐ์กด ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜๋ ค ํ•œ๋‹ค.

๊ธฐ์กด ๋ฐฉ์‹


@Aspect
@Component
public class RateLimitAspect {

    private final Cache<String, Integer> requestCountsPerIpAddress = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES) // ๊ธฐ๋ณธ 1๋ถ„ ๋งŒ๋ฃŒ
            .build();

    private final HttpServletRequest request;

    public RateLimitAspect(HttpServletRequest request) {
        this.request = request;
    }

    @Around("@annotation(rateLimit)") // @RateLimit ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ์— ์ ์šฉ
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String clientIp = getClientIP();
        int maxRequests = rateLimit.maxRequests();
        int timeWindow = rateLimit.timeWindow();

        // ์š”์ฒญ ํšŸ์ˆ˜ ํ™•์ธ ๋ฐ ์—…๋ฐ์ดํŠธ
        Integer requests = requestCountsPerIpAddress.getIfPresent(clientIp);
        if (requests == null) {
            requestCountsPerIpAddress.put(clientIp, 1);
        } else if (requests >= maxRequests) {
            throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS, "Too many requests. Try again later.");
        } else {
            requestCountsPerIpAddress.put(clientIp, requests + 1);
        }

        // ์š”์ฒญ ํ—ˆ์šฉ
        return joinPoint.proceed();
    }

    // ํด๋ผ์ด์–ธํŠธ IP ์ฃผ์†Œ ์ถ”์ถœ
    private String getClientIP() {
        String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader == null) {
            return request.getRemoteAddr();
        }
        return xfHeader.split(",")[0];
    }
}

๊ธฐ์กด ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ Spring AOP๋กœ ๊ตฌ์„ฑ๋˜์—ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ์งš์–ด๋ณด์ž.

์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜ ์š”๊ตฌ์‚ฌํ•ญ

  1. ๊ณต์ •ํ•œ ์š”์ฒญ ์ฒ˜๋ฆฌ
  2. ์„œ๋น„์Šค ๊ฑฐ๋ถ€ ๊ณต๊ฒฉ(DDoS) ๋ฐฉ์–ด
  3. ํšจ์œจ์ ์ธ ํŠธ๋ž˜ํ”ฝ ์ œ์–ด (Rate Control)
  4. ๋น ๋ฅธ ์‘๋‹ต๊ณผ ๋‚ฎ์€ ์˜ค๋ฒ„ํ—ค๋“œ
  5. ์œ ์—ฐํ•œ ์ •์ฑ… ์ ์šฉ

์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜์˜ ๊ณ„์ธต ์„ ํƒ

์ฑ…์—์„œ ์–ธ๊ธ‰ํ•˜๋Š” ์ฒ˜๋ฆฌ์œจ ์ œํ•œ ์žฅ์น˜์˜ ์œ„์น˜๋Š” ํด๋ผ์ด์–ธํŠธ, ์„œ๋ฒ„, ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ์‚ฌ์ด(๋ฏธ๋“ค์›จ์–ด)๋ฅผ ์–ธ๊ธ‰ํ•˜์˜€๋‹ค.

๐Ÿšจ ํด๋ผ์ด์–ธํŠธ ์ธก ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์˜ ๋‹จ์ 

ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์„ ์ ์šฉํ•˜๋ฉด ์„œ๋ฒ„ ๋ถ€ํ•˜๋ฅผ ์ค„์ผ ์ˆ˜ ์—†๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์ œํ•œ์„ ์šฐํšŒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ธŒ๋ผ์šฐ์ €๋‚˜ ์•ฑ์—์„œ ์š”์ฒญ ๋นˆ๋„๋ฅผ ์ œํ•œํ•ด๋„, ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์š”์ฒญ์„ ์กฐ์ž‘ํ•˜๋ฉด ๋ฌด๋ ฅํ™”๋  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, DDoS ๊ณต๊ฒฉ์„ ๋ฐฉ์–ดํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ๊ฐ๊ธฐ ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ œํ•œ ์ •์ฑ…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์ผ๊ด€์„ฑ์ด ๋ถ€์กฑํ•˜๋‹ค.

๐Ÿšจ ์„œ๋ฒ„ ์ธก ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์˜ ๋‹จ์ 

์„œ๋ฒ„์—์„œ ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ค‘์•™์—์„œ ์š”์ฒญ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•œ ํ›„์—์•ผ ๊ฐ์ง€๋˜์–ด ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ์„œ๋ฒ„๋ณ„๋กœ ์ œํ•œ ์ •์ฑ…์ด ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์–ด ์ผ๊ด€๋œ ์ œํ•œ์„ ์ ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด Redis ๊ฐ™์€ ๋ถ„์‚ฐ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ์ถ”๊ฐ€์ ์ธ ๊ฐœ๋ฐœ ๋ฐ ์šด์˜ ๋ถ€๋‹ด์ด ๋ฐœ์ƒํ•œ๋‹ค.

๋ฏธ๋“ค์›จ์–ด์— ๊ตฌ์„ฑํ•˜์ž.

ํด๋ผ์šฐ๋“œ๋ฅผ ์ด์šฉ์‹œ API GateWay๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ณ , Spring์˜ ๊ฒฝ์šฐ SpringFilter๋กœ ์š”์ฒญ์ด dispatcherServlet๋กœ ๊ฐ€๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.

๋ถ„์‚ฐ ํ™˜๊ฒฝ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” API Gateway๊นŒ์ง€๋Š” ๊ตฌ์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค. ๊ทธ๋ ‡๊ธฐ์— Filter๋กœ์˜ ๊ตฌํ˜„์ด ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜„์žฌ AOP๋กœ ๊ตฌ์„ฑํ•œ ๊ฒƒ์€ ๋ฌด์—‡์ด ์ž˜๋ชป๋˜์—ˆ์„๊นŒ?

API Gateway vs AOP vs Filter :: ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€์ ์—์„œ

์š”๊ตฌ์‚ฌํ•ญAPI GatewaySpring FilterSpring AOP
๊ณต์ •ํ•œ ์š”์ฒญ ์ฒ˜๋ฆฌโœ… ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋„ ์ผ๊ด€๋œ ์š”์ฒญ ์ œํ•œ ๊ฐ€๋Šฅโš  ๋‹จ์ผ ์„œ๋ฒ„ ๊ธฐ์ค€์œผ๋กœ ์ ์šฉ (๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ์ถ”๊ฐ€ ๊ตฌ์„ฑ ํ•„์š”)โš  ๊ฐœ๋ณ„ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๋‹จ์œ„๋กœ ์ ์šฉ๋˜๋ฏ€๋กœ ์š”์ฒญ ๋‹จ์œ„ ์ œํ•œ์ด ์–ด๋ ค์›€
DDoS ๋ฐฉ์–ดโœ… WAF(Web Application Firewall) ๋ฐ Rate Limiting ๊ธฐ๋Šฅ ์ œ๊ณตโš  ๊ฐœ๋ณ„ ์„œ๋ฒ„์—์„œ ์ ์šฉ๋˜๋ฏ€๋กœ ์ „์ฒด ์‹œ์Šคํ…œ ์ฐจ์›์˜ ๋ฐฉ์–ด๋Š” ์–ด๋ ค์›€โŒ DDoS ๊ณต๊ฒฉ์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ฐฉ์–ดํ•˜๊ธฐ ์–ด๋ ค์›€
ํšจ์œจ์ ์ธ ํŠธ๋ž˜ํ”ฝ ์ œ์–ด (Rate Control)โœ… Redis ๊ธฐ๋ฐ˜์˜ ๊ธ€๋กœ๋ฒŒ Rate Limit ์ง€์›โœ… ์š”์ฒญ ๋‹จ์œ„๋กœ ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅโš  ์š”์ฒญ์ด ์•„๋‹ˆ๋ผ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๋‹จ์œ„๋กœ ์ œํ•œ๋จ (API ์ˆ˜์ค€ ์ œํ•œ์ด ์•„๋‹˜)
๋น ๋ฅธ ์‘๋‹ต๊ณผ ๋‚ฎ์€ ์˜ค๋ฒ„ํ—ค๋“œโœ… ์™ธ๋ถ€ ์„œ๋น„์Šค์—์„œ ์š”์ฒญ ์ฐจ๋‹จ โ†’ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌ์†Œ์Šค ์†Œ๋น„ ์ตœ์†Œํ™”โœ… ์š”์ฒญ ์ดˆ๊ธฐ์— ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅ โ†’ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์ตœ์†Œํ™”โŒ ์š”์ฒญ์ด ์ด๋ฏธ ์ปจํŠธ๋กค๋Ÿฌ์— ๋„๋‹ฌํ•œ ํ›„ ์ฐจ๋‹จ๋˜๋ฏ€๋กœ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„ ๊ฐ€๋Šฅ
์œ ์—ฐํ•œ ์ •์ฑ… ์ ์šฉโœ… API ๋ณ„, ์‚ฌ์šฉ์ž ๋ณ„, IP ๋ณ„๋กœ ์„ค์ • ๊ฐ€๋Šฅโœ… ํŠน์ • URL ํŒจํ„ด์— ๋Œ€ํ•ด ์ ์šฉ ๊ฐ€๋Šฅโš  ํŠน์ • ๋ฉ”์„œ๋“œ๋ณ„ ์ ์šฉ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, API ์ˆ˜์ค€์˜ ์ •์ฑ… ์ ์šฉ์€ ์–ด๋ ค์›€

์œ„ ํ‘œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋‹จ์ผ(๊ฐœ๋ณ„) ์„œ๋ฒ„์— ์ ์šฉํ•œ๋‹ค๋ฉด SpringFilter๋กœ์˜ ๊ฐœ๋ฐœ์ด ๋‚˜์˜์ง€ ์•Š์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์œ„ ํ‘œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ํ•„ํ„ฐ์™€ AOP์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ทธ๋ฆผ์œผ๋กœ ์ดํ•ดํ•ด๋ณด์ž.

filter์˜ ๊ฒฝ์šฐ Spring์œผ๋กœ ์š”์ฒญ์ด ์ „๋‹ฌ๋˜๊ธฐ ์ „์— ์ œํ•œ๊ทœ์น™์— ๋”ฐ๋ผ ์ œํ•œ๋˜์—ˆ๋‹ค๋ฉด ์š”์ฒญ์„ ์ซ“์•„๋‚ธ๋‹ค. ํ•˜์ง€๋งŒ AOP์˜ ๊ฒฝ์šฐ ์Šคํ”„๋ง ๋‚ด์˜ ์„œ๋น„์Šค ๋ฐ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ณ„์ธต๊นŒ์ง€์˜ ์ ‘๊ทผ์€ ๋ง‰์„์ง€ ๋ชฐ๋ผ๋„ ์Šคํ”„๋ง๊นŒ์ง€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๊ธฐ์— ๋น ๋ฅธ ์‘๋‹ต๊ณผ ๋‚ฎ์€ ์˜ค๋ฒ„ํ—ค๋“œ์˜ ๊ด€์ ์—์„œ ๋ถˆํ•ฉ๊ฒฉ์ด๋‹ค. ์ด์— ๋”ฐ๋ผ ๋‹น์—ฐํžˆ DDOS ๋ฐฉ์–ด์—๋„ ๋ถˆ๋งŒ์กฑ์Šค๋Ÿฝ๋‹ค.

๐Ÿ‹๏ธ ์‹ค์„ธ๊ณ„ ๋น„์œ ๋กœ ์ ‘๊ทผํ•ด๋ณด์ž

์–ด๋Š ํ—ฌ์Šค์žฅ์€ ํšŒ์›๋“ค์˜ ์ผ์ผ ์ด์šฉ์„ "ํ•œ ๋ฒˆ"์œผ๋กœ ์ œํ•œํ•œ๋‹ค.
์ผ๋ฐ˜์ ์ธ ๊ตฌํ˜„ ๋ฐฉ์‹์ด๋ผ๋ฉด, ์ž…๊ตฌ์—์„œ ํšŒ์›๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์ฆ‰์‹œ ์ด์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ ,
์ด๋ฏธ ์ด์šฉํ•œ ๊ฒฝ์šฐ "๊ธˆ์ผ ์ž…์žฅ ๋ถˆ๊ฐ€" ์‘๋‹ต(ํ•„ํ„ฐ ์ฒ˜๋ฆฌ)์„ ๋ฐ›์•„ ์ž…์žฅ์ด ์ฐจ๋‹จ๋œ๋‹ค.


ํ•˜์ง€๋งŒ ๊ธฐ์กด AOP ๋ฐฉ์‹์—์„œ๋Š” ๋ˆ„๊ตฌ๋‚˜ ์ž…๊ตฌ๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ๋‹ค.
์‚ฌ์šฉ์ž๋Š” ํ—ฌ์Šค๋ณต์œผ๋กœ ํ™˜๋ณตํ•˜๊ณ , ๋ฌผ์„ ํ•œ์ž” ๋งˆ์‹œ๊ณ , ์•„๋ น์„ ๋“ค๋ ค๊ณ  ํ•  ๋•Œ์•ผ ๋น„๋กœ์†Œ
"๊ธˆ์ผ ํ—ฌ์Šค์žฅ ์ด์šฉ ๋ถˆ๊ฐ€" ์‘๋‹ต(AOP ์ฒ˜๋ฆฌ)์„ ๋ฐ›๋Š”๋‹ค.


๐Ÿ›  ํ•ต์‹ฌ:
AOP โ†’ Filter๋กœ ๋ณ€๊ฒฝํ•œ ์ด์œ ๋Š” "์–ด์ฐจํ”ผ ๋‚˜๊ฐ€์•ผ ํ•  ์‚ฌ๋žŒ์„ ์•ˆ๊นŒ์ง€ ๋ถˆ๋Ÿฌ๋“ค์—ฌ ๊ฑธ๋Ÿฌ๋‚ด์ง€?" ๋ผ๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.
๋ถˆํ•„์š”ํ•œ ์ž์› ๋‚ญ๋น„ ์—†์ด ์ž…๊ตฌ์—์„œ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ด ๋” ํ•ฉ๋ฆฌ์ ์ด๋‹ค.

ํ•„ํ„ฐ ๊ตฌํ˜„


@Slf4j
@Order(1) // ์ธ์ฆ ํ•„ํ„ฐ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰
public class RateLimitingFilter extends OncePerRequestFilter {

    private static final int MAX_REQUESTS = 10; // ๋ถ„๋‹น ์ตœ๋Œ€ ์š”์ฒญ ์ˆ˜

    private final Cache requestCounts;


    public RateLimitingFilter(CacheManager cacheManager) {
        this.requestCounts = cacheManager.getCache("rateLimitCache");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String key = getRateLimitKey(request);
        Integer count = requestCounts.get(key, Integer.class);

        if (count == null) {
            requestCounts.put(key, 1);
        } else if (count >= MAX_REQUESTS) {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Too Many Requests");
            log.warn("Rate limit exceeded for {}", key);
            return;
        } else {
            requestCounts.put(key, count + 1);
        }

        filterChain.doFilter(request, response);
    }

    /**
     * IP ๊ธฐ๋ฐ˜ Rate Limit ํ‚ค ์ƒ์„ฑ
     * - `X-Forwarded-For` ํ—ค๋”๊ฐ€ ์กด์žฌํ•˜๋ฉด ์ฒซ ๋ฒˆ์งธ IP๋ฅผ ์‚ฌ์šฉ (ํ”„๋ก์‹œ ํ™˜๊ฒฝ ๊ณ ๋ ค)
     * - ์—†๋‹ค๋ฉด `request.getRemoteAddr()` ์‚ฌ์šฉ
     */
    private String getRateLimitKey(HttpServletRequest request) {
        String ip = getClientIp(request);
        return ip + ":" + request.getRequestURI();
    }

    /**
     * ํด๋ผ์ด์–ธํŠธ์˜ ์‹ค์ œ IP ์ฃผ์†Œ ๋ฐ˜ํ™˜
     * - `X-Forwarded-For` ํ—ค๋”๊ฐ€ ์กด์žฌํ•˜๋ฉด ์ฒซ ๋ฒˆ์งธ IP ์‚ฌ์šฉ (ํ”„๋ก์‹œ ํ™˜๊ฒฝ ๊ณ ๋ ค)
     * - ์—†๋‹ค๋ฉด `request.getRemoteAddr()` ์‚ฌ์šฉ
     */
    private String getClientIp(HttpServletRequest request) {
        String forwardedFor = request.getHeader("X-Forwarded-For");
        if (forwardedFor != null && !forwardedFor.isEmpty()) {
            return forwardedFor.split(",")[0].trim(); // ์—ฌ๋Ÿฌ ๊ฐœ์˜ IP๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์‚ฌ์šฉ
        }
        return request.getRemoteAddr();
    }
}

์ฒ˜๋ฆฌ์œจ ์ œํ•œ ๋ฐฉ์‹: ๊ณ ์ • ์œˆ๋„์šฐ ์นด์šดํ„ฐ

๋™์ž‘ ๋ฐฉ์‹

1๏ธโƒฃ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด โž ์บ์‹œ(ehcache)์—์„œ ์นด์šดํŠธ๋ฅผ +1 ์ฆ๊ฐ€ โฌ†๏ธ
2๏ธโƒฃ ์ตœ๋Œ€ ์š”์ฒญ ์ˆ˜ โž 10๊ฐœ๊นŒ์ง€ ํ—ˆ์šฉ
3๏ธโƒฃ ์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ โž ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ์บ์‹œ๋Š” 1๋ถ„๊ฐ„ ์œ ์ง€ โŒ› (๊ณ ์ • ์œˆ๋„์šฐ ๋ฐฉ์‹)
4๏ธโƒฃ ์š”์ฒญ ํšŸ์ˆ˜๊ฐ€ 10์„ ์ดˆ๊ณผํ•˜๋ฉด โž TOO_MANY_REQUESTS ์‘๋‹ต ๋ฐ˜ํ™˜ โš ๏ธ


๐Ÿ“Œ ํŠน์ง•

  • ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•จ โž ์š”์ฒญ ์ˆ˜๋ฅผ ์นด์šดํŒ…ํ•˜๊ณ  ์ œํ•œํ•˜๋Š” ๋ฐฉ์‹
  • ์•ฝ๊ฐ„ ๋Š์Šจํ•œ ๊ทœ์น™ ์ ์šฉ โž ์œˆ๋„์šฐ ๊ฒฝ๊ณ„ ๋ฌธ์ œ(Burst Traffic ๊ฐ€๋Šฅ์„ฑ) ์กด์žฌ
  • ๊ณ ์ •๋œ ์‹œ๊ฐ„(1๋ถ„)๋งˆ๋‹ค ์ดˆ๊ธฐํ™” โž ์ƒˆ๋กœ์šด ์œˆ๋„์šฐ์—์„œ ์นด์šดํŠธ ๋ฆฌ์…‹

๐Ÿšจ ๊ณ ์ • ์œˆ๋„์šฐ ์นด์šดํ„ฐ ๋ฐฉ์‹์˜ ๋‹จ์ 

๐Ÿ“Œ ์˜ˆ์‹œ (๋ฒ„ํ‚ท max ๊ฐ’ = 5)

  • 2:00:00 โ†’ ์บ์‹œ๊ฐ€ ์ดˆ๊ธฐํ™”๋จ
  • 2:00:58 โ†’ 5๊ฐœ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ด (์ œํ•œ ๋‚ด ์š”์ฒญ)
  • 2์ดˆ ๋’ค(2:01:00) โ†’ ์บ์‹œ๊ฐ€ ์ดˆ๊ธฐํ™”๋จ
  • 2:01:01 โ†’ ๋‹ค์‹œ 5๊ฐœ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ด

โžก ์•ฝ 3์ดˆ ๋™์•ˆ ์ด 10๊ฐœ์˜ ์š”์ฒญ์ด ์ฒ˜๋ฆฌ๋จ
โžก ํ•˜์ง€๋งŒ ๋ฒ„ํ‚ท max ๊ฐ’์€ 5์ด๋ฏ€๋กœ, ์›๋ž˜๋Š” ํ—ˆ์šฉ๋˜์ง€ ์•Š์•„์•ผ ํ•˜๋Š” ์š”์ฒญ์ด ํ—ˆ์šฉ๋จ


โš  ์ด ๋ฐฉ์‹์˜ ํ•œ๊ณ„

  • ์‹œ๊ฐ„ ๊ฒฝ๊ณ„(์œˆ๋„์šฐ ๋ฆฌ์…‹) ๋ฌธ์ œ ๋ฐœ์ƒ โ†’ ์งง์€ ์‹œ๊ฐ„ ๋™์•ˆ ๋งŽ์€ ์š”์ฒญ์ด ํ—ˆ์šฉ๋  ์ˆ˜ ์žˆ์Œ
  • ์ •ํ™•ํ•œ ์š”์ฒญ ์ œํ•œ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ ์ ํ•ฉํ•˜์ง€ ์•Š์Œ

โœ… ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š”?

๐Ÿ“Œ ์ฒ˜๋ฆฌ์œจ ์ œํ•œ์ด ์•„์ฃผ ์ •๋ฐ€ํ•  ํ•„์š”๋Š” ์—†์œผ๋ฏ€๋กœ, ๊ตฌํ˜„์ด ์‰ฌ์šด ๊ณ ์ • ์œˆ๋„์šฐ ์นด์šดํ„ฐ ๋ฐฉ์‹์„ ์„ ํƒ
๐Ÿ“Œ ๋ณด๋‹ค ์ •ํ™•ํ•œ ์ œํ•œ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ๋ฐฉ์‹ ๊ณ ๋ ค ๊ฐ€๋Šฅ

profile
์ž๋ฐ”์ง‘์‚ฌ์˜ ๊ฑฐ๋ถ์ด ์ˆ˜๋ จ๋ฒ•

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด