{"id":10958,"date":"2025-10-23T11:24:18","date_gmt":"2025-10-23T02:24:18","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10958"},"modified":"2025-10-23T11:24:18","modified_gmt":"2025-10-23T02:24:18","slug":"spring-boot-api-%ec%a0%91%ec%86%8d-%ea%b1%b4%ec%88%98-%ec%a0%9c%ed%95%9c%ed%95%98%ea%b8%b0","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10958","title":{"rendered":"Spring Boot : API \uc811\uc18d \uac74\uc218 \uc81c\ud55c\ud558\uae30"},"content":{"rendered":"<h1>Spring Boot : API \uc811\uc18d \uac74\uc218 \uc81c\ud55c\ud558\uae30<\/h1>\n<p>DDOS \uacf5\uaca9\uc5d0 \ub300\ube44\ud558\uae30 \uc704\ud574, \uc77c\uc815 \uc811\uc18d \uac74\uc218 \uc774\uc0c1\uc758 API \ud638\ucd9c\uc744 \uc81c\ud55c\ud55c\ub2e4.<\/p>\n<h2>\uc758\uc874\uc131 \ucd94\uac00<\/h2>\n<pre><code class=\"language-gradle\">dependencies {\n    \/\/ ......\n    implementation(&quot;com.bucket4j:bucket4j-core:8.6.0&quot;)\n}<\/code><\/pre>\n<h2>\uc11c\ube44\uc2a4<\/h2>\n<pre><code class=\"language-java\">@Service\n@RequiredArgsConstructor\npublic class RateLimitService {\n    private final Map&lt;String, Bucket&gt; cache = new ConcurrentHashMap&lt;&gt;();\n\n    public boolean allowRequest(String key) {\n        Bucket bucket = cache.computeIfAbsent(key, k -&gt; {\n            \/\/ \uc544\uc774\ud53c\ub2f9 \ubd84\ub2f9 20\ud68c \uc81c\ud55c\n            Bandwidth limit = Bandwidth.builder()\n                    .capacity(20)\n                    .refillIntervally(20, Duration.ofMinutes(1))\n                    .build();\n\n            return Bucket.builder()\n                    .addLimit(limit)\n                    .build();\n        });\n\n        return bucket.tryConsume(1);\n    }\n}<\/code><\/pre>\n<h2>\ucee8\ud2b8\ub864\ub7ec<\/h2>\n<pre><code class=\"language-java\">    @PostMapping(&quot;\/myApi&quot;)\n    public ResponseEntity&lt;?&gt; myApi(@Valid @RequestBody MyApiRequest request, HttpServletRequest httpRequest) {\n        String clientKey = getClientIpAddress(httpRequest);\n\n        if (!rateLimitService.allowRequest(&quot;refresh:&quot; + clientKey)) {\n            throw new TooManyAccessException(&quot;\ub108\ubb34 \ub9ce\uc740 \uc694\uccad\uc785\ub2c8\ub2e4. \uc7a0\uc2dc \ud6c4 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.&quot;);\n        }\n\n        \/\/ ......\n    }\n\n    private String getClientIpAddress(HttpServletRequest request) {\n        String xForwardedFor = request.getHeader(&quot;X-Forwarded-For&quot;);\n        if (xForwardedFor != null &amp;&amp; !xForwardedFor.isEmpty()) {\n            return xForwardedFor.split(&quot;,&quot;)[0].trim();\n        }\n\n        String xRealIp = request.getHeader(&quot;X-Real-IP&quot;);\n        if (xRealIp != null &amp;&amp; !xRealIp.isEmpty()) {\n            return xRealIp;\n        }\n\n        return request.getRemoteAddr();\n    }<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Spring Boot : API \uc811\uc18d \uac74\uc218 \uc81c\ud55c\ud558\uae30 DDOS \uacf5\uaca9\uc5d0 \ub300\ube44\ud558\uae30 \uc704\ud574, \uc77c\uc815 \uc811\uc18d \uac74\uc218 \uc774\uc0c1\uc758 API \ud638\ucd9c\uc744 \uc81c\ud55c\ud55c\ub2e4. \uc758\uc874\uc131 \ucd94\uac00 dependencies { \/\/ &#8230;&#8230; implementation(&quot;com.bucket4j:bucket4j-core:8.6.0&quot;) } \uc11c\ube44\uc2a4 @Service @RequiredArgsConstructor public class RateLimitService { private final Map&lt;String, Bucket&gt; cache = new ConcurrentHashMap&lt;&gt;(); public boolean allowRequest(String key) { Bucket bucket = cache.computeIfAbsent(key, k -&gt; { \/\/ \uc544\uc774\ud53c\ub2f9\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=10958\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-10958","post","type-post","status-publish","format-standard","hentry","category-java"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10958","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10958"}],"version-history":[{"count":1,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10958\/revisions"}],"predecessor-version":[{"id":10959,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/10958\/revisions\/10959"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10958"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10958"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10958"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}