{"id":11417,"date":"2026-02-23T13:42:50","date_gmt":"2026-02-23T04:42:50","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11417"},"modified":"2026-02-23T13:42:50","modified_gmt":"2026-02-23T04:42:50","slug":"spring-boot-%eb%a9%b1%eb%93%b1%ec%84%b1idempotency%ea%b3%bc-%eb%a9%b1%eb%93%b1%ed%82%a4-%ed%99%9c%ec%9a%a9%ed%95%98%ea%b8%b0","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11417","title":{"rendered":"Spring Boot &#8211; \uba71\ub4f1\uc131(Idempotency)\uacfc \uba71\ub4f1\ud0a4 \ud65c\uc6a9\ud558\uae30"},"content":{"rendered":"<h1>Spring Boot &#8211; \uba71\ub4f1\uc131(Idempotency)\uacfc \uba71\ub4f1\ud0a4 \ud65c\uc6a9\ud558\uae30<\/h1>\n<p>\ub124\ud2b8\uc6cc\ud06c \uc624\ub958\ub85c \uc778\ud574 \uacb0\uc81c \uc694\uccad\uc774 \ub450 \ubc88 \uac00\uac70\ub098, \uc2e4\uc218\ub85c \ubc84\ud2bc\uc744 \uc5ec\ub7ec \ubc88 \ud074\ub9ad\ud574\uc11c \uc911\ubcf5 \uc8fc\ubb38\uc774 \ubc1c\uc0dd\ud55c \uc801\uc774 \uc788\ub098\uc694? \uc774\ub7ec\ud55c \ubb38\uc81c\ub97c \uc6b0\uc544\ud558\uac8c \ud574\uacb0\ud574 \uc8fc\ub294 \ud575\uc2ec \uae30\uc220\uc774 \ubc14\ub85c <strong>\uba71\ub4f1\ud0a4(Idempotency Key)<\/strong>\uc785\ub2c8\ub2e4.<\/p>\n<h2>1. \uba71\ub4f1\uc131(Idempotency)\uc774\ub780?<\/h2>\n<p><strong>\uba71\ub4f1\uc131<\/strong>\uc774\ub780 \uc5f0\uc0b0\uc744 \uc5ec\ub7ec \ubc88 \uc801\uc6a9\ud558\ub354\ub77c\ub3c4 \uacb0\uacfc\uac00 \ub2ec\ub77c\uc9c0\uc9c0 \uc54a\ub294 \uc131\uc9c8\uc744 \uc758\ubbf8\ud569\ub2c8\ub2e4.<\/p>\n<ul>\n<li><strong>\uc218\ud559\uc801 \uc608\uc2dc:<\/strong> \uc5b4\ub5a4 \uc218\uc5d0 1\uc744 \uacf1\ud558\ub294 \uc5f0\uc0b0\uc740 \uc5ec\ub7ec \ubc88 \ubc18\ubcf5\ud574\ub3c4 \uacb0\uacfc\uac00 \uac19\uc2b5\ub2c8\ub2e4.<\/li>\n<li><strong>HTTP \uba54\uc11c\ub4dc \uc608\uc2dc:<\/strong> * <code>GET<\/code>, <code>PUT<\/code>, <code>DELETE<\/code>\ub294 \uae30\ubcf8\uc801\uc73c\ub85c \uba71\ub4f1\uc131\uc744 \uac00\uc9d1\ub2c8\ub2e4.<\/li>\n<li><code>POST<\/code>\ub294 \ud638\ucd9c\ud560 \ub54c\ub9c8\ub2e4 \uc0c8\ub85c\uc6b4 \ub9ac\uc18c\uc2a4\ub97c \uc0dd\uc131\ud560 \uc218 \uc788\uc73c\ubbc0\ub85c \uae30\ubcf8\uc801\uc73c\ub85c <strong>\ube44\uba71\ub4f1(Non-idempotent)<\/strong>\ud569\ub2c8\ub2e4.<\/li>\n<\/ul>\n<h2>2. \uba71\ub4f1\ud0a4(Idempotency Key)\uc758 \uc5ed\ud560<\/h2>\n<p><code>POST<\/code>\uc640 \uac19\uc774 \ube44\uba71\ub4f1\ud55c \uc694\uccad\uc744 \uc548\uc804\ud558\uac8c \ucc98\ub9ac\ud558\uae30 \uc704\ud574 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 \uc694\uccad \ud5e4\ub354\uc5d0 <strong>\uc720\ub2c8\ud06c\ud55c \uac12(UUID \ub4f1)<\/strong>\uc744 \ub2f4\uc544 \ubcf4\ub0c5\ub2c8\ub2e4. \uc11c\ubc84\ub294 \uc774 \ud0a4\ub97c \ud655\uc778\ud558\uc5ec:<\/p>\n<ol>\n<li><strong>\ucc98\uc74c \ubcf8 \ud0a4\ub77c\uba74:<\/strong> \uc694\uccad\uc744 \uc815\uc0c1 \ucc98\ub9ac\ud558\uace0 \uacb0\uacfc\ub97c \uc800\uc7a5\ud569\ub2c8\ub2e4.<\/li>\n<li><strong>\uc774\ubbf8 \ucc98\ub9ac\ub41c \ud0a4\ub77c\uba74:<\/strong> \uc2e4\uc81c \ub85c\uc9c1\uc744 \uc218\ud589\ud558\uc9c0 \uc54a\uace0 \uc800\uc7a5\ub41c \uacb0\uacfc(\uc751\ub2f5)\ub97c \uadf8\ub300\ub85c \ubc18\ud658\ud569\ub2c8\ub2e4.<\/li>\n<\/ol>\n<h2>3. Spring Boot\uc5d0\uc11c \uba71\ub4f1\ud0a4 \uad6c\ud604\ud558\uae30<\/h2>\n<p>Spring Boot \ud658\uacbd\uc5d0\uc11c \uba71\ub4f1\ud0a4\ub97c \uad6c\ud604\ud558\ub294 \uac00\uc7a5 \ud6a8\uc728\uc801\uc778 \ubc29\ubc95\uc740 <strong>Redis<\/strong>\uc640 <strong>Interceptor<\/strong>\ub97c \ud65c\uc6a9\ud558\ub294 \uac83\uc785\ub2c8\ub2e4.<\/p>\n<h3>A. \uae30\uc220 \uc2a4\ud0dd<\/h3>\n<ul>\n<li><strong>Redis:<\/strong> \ubd84\uc0b0 \ud658\uacbd\uc5d0\uc11c \ud0a4\ub97c \ube60\ub974\uac8c \uc870\ud68c\ud558\uace0 \ub9cc\ub8cc \uc2dc\uac04(TTL)\uc744 \uc124\uc815\ud558\uae30\uc5d0 \uc801\ud569\ud569\ub2c8\ub2e4.<\/li>\n<li><strong>HandlerInterceptor:<\/strong> \ucee8\ud2b8\ub864\ub7ec\uc5d0 \uc9c4\uc785\ud558\uae30 \uc804 \uba71\ub4f1\ud0a4\ub97c \uac80\uc0ac\ud569\ub2c8\ub2e4.<\/li>\n<\/ul>\n<h3>B. \ud750\ub984\ub3c4<\/h3>\n<ol>\n<li>\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 <code>Idempotency-Key<\/code>\ub97c \ud5e4\ub354\uc5d0 \ub2f4\uc544 \uc694\uccad.<\/li>\n<li>\uc11c\ubc84(Interceptor)\uac00 Redis\uc5d0\uc11c \ud574\ub2f9 \ud0a4 \uc874\uc7ac \uc5ec\ubd80 \ud655\uc778.<\/li>\n<li><strong>Key \uc5c6\uc74c:<\/strong> Redis\uc5d0 \ud0a4 \uc800\uc7a5 (Status: <code>PROCESSING<\/code>) -&gt; \ube44\uc988\ub2c8\uc2a4 \ub85c\uc9c1 \uc218\ud589 -&gt; \uacb0\uacfc \uc800\uc7a5 \ubc0f \ubc18\ud658.<\/li>\n<li><strong>Key \uc788\uc74c:<\/strong> * <code>PROCESSING<\/code> \uc911\uc774\uba74 &quot;\ucc98\ub9ac \uc911&quot; \uc5d0\ub7ec \ubc18\ud658.\n<ul>\n<li>\uc644\ub8cc\ub41c \uc0c1\ud0dc\uba74 \uc800\uc7a5\ub41c \uc751\ub2f5\uac12 \ubc18\ud658.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h3>C. \uc8fc\uc694 \ucf54\ub4dc \uc608\uc2dc<\/h3>\n<h4>1. Redis \uc124\uc815 \ubc0f \ub85c\uc9c1 (Pseudo Code)<\/h4>\n<pre><code class=\"language-java\">@Component\n@RequiredArgsConstructor\npublic class IdempotencyService {\n    private final RedisTemplate&lt;String, Object&gt; redisTemplate;\n\n    public boolean isFirstRequest(String key) {\n        \/\/ SETNX (Set if Not Exists)\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc6d0\uc790\uc801 \uccb4\ud06c\n        return redisTemplate.opsForValue()\n            .setIfAbsent(key, &quot;PROCESSING&quot;, Duration.ofMinutes(10));\n    }\n\n    public void saveResponse(String key, Object response) {\n        redisTemplate.opsForValue().set(key, response, Duration.ofMinutes(60));\n    }\n}\n<\/code><\/pre>\n<h4>2. Interceptor \uad6c\ud604<\/h4>\n<pre><code class=\"language-java\">@Component\n@RequiredArgsConstructor\npublic class IdempotencyInterceptor implements HandlerInterceptor {\n    private final IdempotencyService idempotencyService;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        String idempotencyKey = request.getHeader(&quot;Idempotency-Key&quot;);\n\n        if (idempotencyKey == null) return true; \/\/ \ud544\uc218\uac00 \uc544\ub2c8\ub77c\uba74 \ud1b5\uacfc\n\n        if (!idempotencyService.isFirstRequest(idempotencyKey)) {\n            \/\/ \uc774\ubbf8 \uc874\uc7ac\ud558\ub294 \ud0a4\ub77c\uba74 \uc911\ubcf5 \uc694\uccad \ucc98\ub9ac (409 Conflict \ub610\ub294 \uae30\uc874 \uc751\ub2f5 \ubc18\ud658)\n            response.setStatus(HttpServletResponse.SC_CONFLICT);\n            return false;\n        }\n        return true;\n    }\n}\n<\/code><\/pre>\n<hr \/>\n<h2>4. \ub3c4\uc785 \uc2dc \uc8fc\uc758\uc0ac\ud56d<\/h2>\n<ul>\n<li><strong>\ud0a4\uc758 \ubc94\uc704:<\/strong> \uc720\uc800 ID\uc640 \uba71\ub4f1\ud0a4\ub97c \uc870\ud569\ud558\uc5ec \ud0a4\ub97c \uc0dd\uc131\ud558\ub294 \uac83\uc774 \uc548\uc804\ud569\ub2c8\ub2e4. (\ub2e4\ub978 \uc720\uc800\uc640 \ud0a4\uac00 \uacb9\uce60 \ud655\ub960 \ubc29\uc9c0)<\/li>\n<li><strong>\ub9cc\ub8cc \uc2dc\uac04(TTL):<\/strong> \uba71\ub4f1\ud0a4\ub97c \ubb34\ud55c\ud788 \uc800\uc7a5\ud560 \uc218\ub294 \uc5c6\uc73c\ubbc0\ub85c, \ube44\uc988\ub2c8\uc2a4 \ud2b9\uc131\uc5d0 \ub9de\ucdb0(\uc608: 24\uc2dc\uac04) \uc801\uc808\ud55c \uc720\ud6a8 \uae30\uac04\uc744 \uc124\uc815\ud574\uc57c \ud569\ub2c8\ub2e4.<\/li>\n<li><strong>\uc131\uacf5\/\uc2e4\ud328 \ucf00\uc774\uc2a4:<\/strong> \ube44\uc988\ub2c8\uc2a4 \ub85c\uc9c1\uc774 \uc2e4\ud328\ud588\uc744 \uacbd\uc6b0, \uba71\ub4f1\ud0a4\ub97c \uc0ad\uc81c\ud558\uc5ec \uc7ac\uc2dc\ub3c4\ud560 \uc218 \uc788\uac8c \ud560\uc9c0 \uc544\ub2c8\uba74 \uc2e4\ud328 \uae30\ub85d\uc744 \ub0a8\uae38\uc9c0 \uc815\ucc45\uc744 \uc815\ud574\uc57c \ud569\ub2c8\ub2e4.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Spring Boot &#8211; \uba71\ub4f1\uc131(Idempotency)\uacfc \uba71\ub4f1\ud0a4 \ud65c\uc6a9\ud558\uae30 \ub124\ud2b8\uc6cc\ud06c \uc624\ub958\ub85c \uc778\ud574 \uacb0\uc81c \uc694\uccad\uc774 \ub450 \ubc88 \uac00\uac70\ub098, \uc2e4\uc218\ub85c \ubc84\ud2bc\uc744 \uc5ec\ub7ec \ubc88 \ud074\ub9ad\ud574\uc11c \uc911\ubcf5 \uc8fc\ubb38\uc774 \ubc1c\uc0dd\ud55c \uc801\uc774 \uc788\ub098\uc694? \uc774\ub7ec\ud55c \ubb38\uc81c\ub97c \uc6b0\uc544\ud558\uac8c \ud574\uacb0\ud574 \uc8fc\ub294 \ud575\uc2ec \uae30\uc220\uc774 \ubc14\ub85c \uba71\ub4f1\ud0a4(Idempotency Key)\uc785\ub2c8\ub2e4. 1. \uba71\ub4f1\uc131(Idempotency)\uc774\ub780? \uba71\ub4f1\uc131\uc774\ub780 \uc5f0\uc0b0\uc744 \uc5ec\ub7ec \ubc88 \uc801\uc6a9\ud558\ub354\ub77c\ub3c4 \uacb0\uacfc\uac00 \ub2ec\ub77c\uc9c0\uc9c0 \uc54a\ub294 \uc131\uc9c8\uc744 \uc758\ubbf8\ud569\ub2c8\ub2e4. \uc218\ud559\uc801 \uc608\uc2dc: \uc5b4\ub5a4 \uc218\uc5d0 1\uc744 \uacf1\ud558\ub294 \uc5f0\uc0b0\uc740 \uc5ec\ub7ec \ubc88\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11417\">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":[3],"tags":[],"class_list":["post-11417","post","type-post","status-publish","format-standard","hentry","category-spring-boot"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11417","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=11417"}],"version-history":[{"count":1,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11417\/revisions"}],"predecessor-version":[{"id":11418,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11417\/revisions\/11418"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}