@portone/mcp-server 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (576) hide show
  1. package/LICENSE-APACHE +201 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +165 -0
  4. package/assets/docs/README.md +27 -0
  5. package/assets/docs/api/backward-compatibility.md +40 -0
  6. package/assets/docs/blog/posts/2024-02/v2-oom.md +341 -0
  7. package/assets/docs/blog/posts/2024-02/v2-webhook.md +314 -0
  8. package/assets/docs/blog/posts/2024-02/v2-zio.md +529 -0
  9. package/assets/docs/blog/posts/2024-03/tgs.md +238 -0
  10. package/assets/docs/blog/posts/2024-04/gcp-datastream.md +307 -0
  11. package/assets/docs/blog/posts/2024-04/solid.md +558 -0
  12. package/assets/docs/blog/posts/2024-07/v2-event-sourcing-basic.md +231 -0
  13. package/assets/docs/help/admin-console/analytics/content32.md +270 -0
  14. package/assets/docs/help/admin-console/console-account/content31.md +94 -0
  15. package/assets/docs/help/admin-console/console_home/content34.md +82 -0
  16. package/assets/docs/help/admin-console/integration/content28.md +79 -0
  17. package/assets/docs/help/admin-console/payment-service/content29.md +260 -0
  18. package/assets/docs/help/admin-console/pg-settlement/content30.md +352 -0
  19. package/assets/docs/help/admin-console/platform-settlement/content33-1.md +201 -0
  20. package/assets/docs/help/api-sdk-2024-12-19.md +64 -0
  21. package/assets/docs/help/api-sdk-2025-01-15.md +84 -0
  22. package/assets/docs/help/applepay.md +46 -0
  23. package/assets/docs/help/content100000.md +46 -0
  24. package/assets/docs/help/content100002.md +52 -0
  25. package/assets/docs/help/content100004.md +29 -0
  26. package/assets/docs/help/content100014.md +50 -0
  27. package/assets/docs/help/content100015.md +20 -0
  28. package/assets/docs/help/content100016.md +28 -0
  29. package/assets/docs/help/content100020.md +28 -0
  30. package/assets/docs/help/content100022.md +40 -0
  31. package/assets/docs/help/content100023.md +21 -0
  32. package/assets/docs/help/content100024.md +25 -0
  33. package/assets/docs/help/content100025.md +40 -0
  34. package/assets/docs/help/content100026.md +20 -0
  35. package/assets/docs/help/content100027.md +24 -0
  36. package/assets/docs/help/content100029.md +29 -0
  37. package/assets/docs/help/content100030.md +82 -0
  38. package/assets/docs/help/content100033.md +44 -0
  39. package/assets/docs/help/content100035.md +34 -0
  40. package/assets/docs/help/content100036.md +14 -0
  41. package/assets/docs/help/content100037.md +16 -0
  42. package/assets/docs/help/content100038.md +32 -0
  43. package/assets/docs/help/content100039.md +28 -0
  44. package/assets/docs/help/content100040.md +31 -0
  45. package/assets/docs/help/content100041.md +37 -0
  46. package/assets/docs/help/content100044.md +15 -0
  47. package/assets/docs/help/content100045.md +21 -0
  48. package/assets/docs/help/content100046.md +34 -0
  49. package/assets/docs/help/content100047.md +32 -0
  50. package/assets/docs/help/content100048.md +25 -0
  51. package/assets/docs/help/content100049.md +20 -0
  52. package/assets/docs/help/content100052.md +147 -0
  53. package/assets/docs/help/content100053.md +40 -0
  54. package/assets/docs/help/content100054.md +18 -0
  55. package/assets/docs/help/content100055.md +37 -0
  56. package/assets/docs/help/content100056.md +15 -0
  57. package/assets/docs/help/content100057.md +28 -0
  58. package/assets/docs/help/content100058.md +36 -0
  59. package/assets/docs/help/content100059.md +23 -0
  60. package/assets/docs/help/content100060.md +43 -0
  61. package/assets/docs/help/content200001.md +21 -0
  62. package/assets/docs/help/content200002.md +38 -0
  63. package/assets/docs/help/content200005.md +30 -0
  64. package/assets/docs/help/content200012.md +91 -0
  65. package/assets/docs/help/content200013.md +52 -0
  66. package/assets/docs/help/content200015.md +41 -0
  67. package/assets/docs/help/content200016.md +30 -0
  68. package/assets/docs/help/content200017.md +39 -0
  69. package/assets/docs/help/content200018.md +105 -0
  70. package/assets/docs/help/content200019.md +59 -0
  71. package/assets/docs/help/content200020.md +42 -0
  72. package/assets/docs/help/content200023.md +54 -0
  73. package/assets/docs/help/content200024.md +49 -0
  74. package/assets/docs/help/content200025.md +68 -0
  75. package/assets/docs/help/content200027.md +34 -0
  76. package/assets/docs/help/content200028.md +70 -0
  77. package/assets/docs/help/content200029.md +71 -0
  78. package/assets/docs/help/content200030.md +67 -0
  79. package/assets/docs/help/content200032.md +39 -0
  80. package/assets/docs/help/content200033.md +21 -0
  81. package/assets/docs/help/content200034.md +34 -0
  82. package/assets/docs/help/content200037.md +38 -0
  83. package/assets/docs/help/content200038.md +50 -0
  84. package/assets/docs/help/content200039.md +45 -0
  85. package/assets/docs/help/content200040.md +15 -0
  86. package/assets/docs/help/content200043.md +23 -0
  87. package/assets/docs/help/content200044.md +33 -0
  88. package/assets/docs/help/content200045.md +25 -0
  89. package/assets/docs/help/content200046.md +104 -0
  90. package/assets/docs/help/content200048.md +34 -0
  91. package/assets/docs/help/content200049.md +54 -0
  92. package/assets/docs/help/content200050.md +43 -0
  93. package/assets/docs/help/content200051.md +81 -0
  94. package/assets/docs/help/content51.md +38 -0
  95. package/assets/docs/help/content61.md +165 -0
  96. package/assets/docs/help/core-service/channel-setting/danal.md +170 -0
  97. package/assets/docs/help/core-service/channel-setting/daou.md +118 -0
  98. package/assets/docs/help/core-service/channel-setting/eximbay.md +136 -0
  99. package/assets/docs/help/core-service/channel-setting/inicis.md +340 -0
  100. package/assets/docs/help/core-service/channel-setting/kakaopay.md +107 -0
  101. package/assets/docs/help/core-service/channel-setting/kcp_channel.md +235 -0
  102. package/assets/docs/help/core-service/channel-setting/kicc.md +114 -0
  103. package/assets/docs/help/core-service/channel-setting/kpn.md +93 -0
  104. package/assets/docs/help/core-service/channel-setting/ksnet.md +116 -0
  105. package/assets/docs/help/core-service/channel-setting/mobilians.md +107 -0
  106. package/assets/docs/help/core-service/channel-setting/naverpay.md +179 -0
  107. package/assets/docs/help/core-service/channel-setting/nice.md +210 -0
  108. package/assets/docs/help/core-service/channel-setting/payco.md +124 -0
  109. package/assets/docs/help/core-service/channel-setting/paymentwall.md +107 -0
  110. package/assets/docs/help/core-service/channel-setting/paypal.md +247 -0
  111. package/assets/docs/help/core-service/channel-setting/settle.md +123 -0
  112. package/assets/docs/help/core-service/channel-setting/smartro.md +164 -0
  113. package/assets/docs/help/core-service/channel-setting/smilepay.md +83 -0
  114. package/assets/docs/help/core-service/channel-setting/tosspay.md +168 -0
  115. package/assets/docs/help/core-service/channel-setting/tosspayments.md +232 -0
  116. package/assets/docs/help/core-service/channel-setting/welcome.md +110 -0
  117. package/assets/docs/help/core-service/how-to-use/additional-danal.md +71 -0
  118. package/assets/docs/help/core-service/how-to-use/additional-daou.md +65 -0
  119. package/assets/docs/help/core-service/how-to-use/additional-eximbay.md +54 -0
  120. package/assets/docs/help/core-service/how-to-use/additional-inicis.md +97 -0
  121. package/assets/docs/help/core-service/how-to-use/additional-kakaopay.md +56 -0
  122. package/assets/docs/help/core-service/how-to-use/additional-kcp.md +53 -0
  123. package/assets/docs/help/core-service/how-to-use/additional-kicc.md +67 -0
  124. package/assets/docs/help/core-service/how-to-use/additional-ksnet.md +68 -0
  125. package/assets/docs/help/core-service/how-to-use/additional-mobilians.md +71 -0
  126. package/assets/docs/help/core-service/how-to-use/additional-naverpay-order.md +69 -0
  127. package/assets/docs/help/core-service/how-to-use/additional-naverpay.md +72 -0
  128. package/assets/docs/help/core-service/how-to-use/additional-nice.md +112 -0
  129. package/assets/docs/help/core-service/how-to-use/additional-payco.md +68 -0
  130. package/assets/docs/help/core-service/how-to-use/additional-settle.md +70 -0
  131. package/assets/docs/help/core-service/how-to-use/additional-smartro.md +70 -0
  132. package/assets/docs/help/core-service/how-to-use/additional-tosspay.md +57 -0
  133. package/assets/docs/help/core-service/how-to-use/additional-tosspayments.md +47 -0
  134. package/assets/docs/help/core-service/how-to-use/additional-welcome.md +72 -0
  135. package/assets/docs/help/core-service/how-to-use/cancel-deadline-by-paymethod.md +68 -0
  136. package/assets/docs/help/core-service/how-to-use/cancel-guide-using-admin-console.md +49 -0
  137. package/assets/docs/help/core-service/how-to-use/cancel-reversal.md +114 -0
  138. package/assets/docs/help/core-service/how-to-use/cellphone-identity-verification.md +62 -0
  139. package/assets/docs/help/core-service/how-to-use/content200000.md +72 -0
  140. package/assets/docs/help/core-service/how-to-use/content200009.md +83 -0
  141. package/assets/docs/help/core-service/how-to-use/danal-contract.md +82 -0
  142. package/assets/docs/help/core-service/how-to-use/daou-contract.md +82 -0
  143. package/assets/docs/help/core-service/how-to-use/eximbay-contract.md +75 -0
  144. package/assets/docs/help/core-service/how-to-use/high-risk-industry.md +554 -0
  145. package/assets/docs/help/core-service/how-to-use/identity-verification-value.md +61 -0
  146. package/assets/docs/help/core-service/how-to-use/impossible-partial-cancel.md +53 -0
  147. package/assets/docs/help/core-service/how-to-use/information-pg.md +151 -0
  148. package/assets/docs/help/core-service/how-to-use/inicis-contract.md +95 -0
  149. package/assets/docs/help/core-service/how-to-use/inicis-vat.md +45 -0
  150. package/assets/docs/help/core-service/how-to-use/inicis-vbank-refund.md +45 -0
  151. package/assets/docs/help/core-service/how-to-use/kakao-contract.md +73 -0
  152. package/assets/docs/help/core-service/how-to-use/kakaopay-contract.md +41 -0
  153. package/assets/docs/help/core-service/how-to-use/kcp-contract.md +82 -0
  154. package/assets/docs/help/core-service/how-to-use/kcp-vat.md +39 -0
  155. package/assets/docs/help/core-service/how-to-use/kcp-vbank-refund.md +46 -0
  156. package/assets/docs/help/core-service/how-to-use/kicc-contract.md +81 -0
  157. package/assets/docs/help/core-service/how-to-use/ksnet-contract.md +78 -0
  158. package/assets/docs/help/core-service/how-to-use/mobilians-contract.md +81 -0
  159. package/assets/docs/help/core-service/how-to-use/naverpay-contract.md +86 -0
  160. package/assets/docs/help/core-service/how-to-use/naverpay-order-1.md +36 -0
  161. package/assets/docs/help/core-service/how-to-use/naverpay-order-contract.md +88 -0
  162. package/assets/docs/help/core-service/how-to-use/naverpay-order-diff.md +66 -0
  163. package/assets/docs/help/core-service/how-to-use/naverpay-order-review-items.md +66 -0
  164. package/assets/docs/help/core-service/how-to-use/naverpay-review-items.md +199 -0
  165. package/assets/docs/help/core-service/how-to-use/nice-contract.md +81 -0
  166. package/assets/docs/help/core-service/how-to-use/open-immediately.md +60 -0
  167. package/assets/docs/help/core-service/how-to-use/other-hosting-company.md +38 -0
  168. package/assets/docs/help/core-service/how-to-use/payco-contract.md +74 -0
  169. package/assets/docs/help/core-service/how-to-use/payment-settlement-method.md +30 -0
  170. package/assets/docs/help/core-service/how-to-use/paymentwall-contract.md +85 -0
  171. package/assets/docs/help/core-service/how-to-use/paypal-contract.md +66 -0
  172. package/assets/docs/help/core-service/how-to-use/point-service.md +293 -0
  173. package/assets/docs/help/core-service/how-to-use/refund-date.md +66 -0
  174. package/assets/docs/help/core-service/how-to-use/requirements.md +100 -0
  175. package/assets/docs/help/core-service/how-to-use/settle-contract.md +79 -0
  176. package/assets/docs/help/core-service/how-to-use/shopify-available-list.md +65 -0
  177. package/assets/docs/help/core-service/how-to-use/shopify-guide.md +62 -0
  178. package/assets/docs/help/core-service/how-to-use/small-business-commission-fee.md +55 -0
  179. package/assets/docs/help/core-service/how-to-use/smartro-contract.md +77 -0
  180. package/assets/docs/help/core-service/how-to-use/test-mode-cancel.md +63 -0
  181. package/assets/docs/help/core-service/how-to-use/tosspay-contract.md +82 -0
  182. package/assets/docs/help/core-service/how-to-use/tosspayments-contract.md +82 -0
  183. package/assets/docs/help/core-service/how-to-use/welcome-contract.md +81 -0
  184. package/assets/docs/help/core-service/how-to-use/withsoft-guide.md +69 -0
  185. package/assets/docs/help/core-service/how-to-use/wordpress-available-pg.md +49 -0
  186. package/assets/docs/help/core-service/how-to-use/wordpress-setting.md +50 -0
  187. package/assets/docs/help/core-service/payment-type/billing-key.md +72 -0
  188. package/assets/docs/help/core-service/payment-type/content200003.md +39 -0
  189. package/assets/docs/help/core-service/payment-type/content200007.md +176 -0
  190. package/assets/docs/help/core-service/payment-type/content200036.md +37 -0
  191. package/assets/docs/help/core-service/payment-type/content200042.md +150 -0
  192. package/assets/docs/help/core-service/payment-type/content200047.md +215 -0
  193. package/assets/docs/help/core-service/payment-type/content200052.md +142 -0
  194. package/assets/docs/help/core-service/payment-type/content200053.md +128 -0
  195. package/assets/docs/help/core-service/payment-type/crossborder.md +132 -0
  196. package/assets/docs/help/core-service/payment-type/eximbay-international.md +96 -0
  197. package/assets/docs/help/core-service/payment-type/hub-service-fee.md +79 -0
  198. package/assets/docs/help/core-service/payment-type/inicis-easypay-hub-fee.md +45 -0
  199. package/assets/docs/help/core-service/payment-type/inicis-international.md +65 -0
  200. package/assets/docs/help/core-service/payment-type/internationl-high-risk-industry.md +267 -0
  201. package/assets/docs/help/core-service/payment-type/internationl-pg.md +42 -0
  202. package/assets/docs/help/core-service/payment-type/kcp-easypay-hub-apply.md +49 -0
  203. package/assets/docs/help/core-service/payment-type/kcp-international.md +60 -0
  204. package/assets/docs/help/core-service/payment-type/nice-easypay-hub-fee.md +72 -0
  205. package/assets/docs/help/core-service/payment-type/nice-international.md +51 -0
  206. package/assets/docs/help/core-service/payment-type/paymentwall-international.md +84 -0
  207. package/assets/docs/help/core-service/payment-type/paypal-international.md +86 -0
  208. package/assets/docs/help/core-service/payment-type/paypal-payment-status.md +55 -0
  209. package/assets/docs/help/core-service/payment-type/schedule-logic-guide.md +49 -0
  210. package/assets/docs/help/core-service/payment-type/schedule-requirement.md +111 -0
  211. package/assets/docs/help/core-service/payment-type/schedule-validity-guide.md +68 -0
  212. package/assets/docs/help/core-service/payment-type/tosspayment-international.md +54 -0
  213. package/assets/docs/help/core-service/payment-type/tosspayments-easypay-hub-fee.md +42 -0
  214. package/assets/docs/help/core-service/payment-type/webhook-re-send.md +41 -0
  215. package/assets/docs/help/faq/console-faq/2-depth-auth.md +53 -0
  216. package/assets/docs/help/faq/console-faq/add-admin-account.md +42 -0
  217. package/assets/docs/help/faq/console-faq/change-console-url.md +51 -0
  218. package/assets/docs/help/faq/console-faq/impossible-login.md +30 -0
  219. package/assets/docs/help/faq/console-faq/payment-list.md +45 -0
  220. package/assets/docs/help/faq/console-faq/sub-store-mgnt.md +97 -0
  221. package/assets/docs/help/faq/general-inquiry/change-business-number.md +32 -0
  222. package/assets/docs/help/faq/general-inquiry/confirm-process.md +118 -0
  223. package/assets/docs/help/faq/general-inquiry/content200004.md +55 -0
  224. package/assets/docs/help/faq/general-inquiry/content200006.md +86 -0
  225. package/assets/docs/help/faq/general-inquiry/content200011.md +24 -0
  226. package/assets/docs/help/faq/general-inquiry/content200031.md +194 -0
  227. package/assets/docs/help/faq/general-inquiry/content200041.md +21 -0
  228. package/assets/docs/help/faq/general-inquiry/contract-checkpoint.md +49 -0
  229. package/assets/docs/help/faq/general-inquiry/main-user.md +56 -0
  230. package/assets/docs/help/faq/general-inquiry/platform-contract-method.md +67 -0
  231. package/assets/docs/help/faq/general-inquiry/platform-contract-pg.md +234 -0
  232. package/assets/docs/help/faq/general-inquiry/portone-advantages.md +71 -0
  233. package/assets/docs/help/faq/general-inquiry/real-channel-info.md +70 -0
  234. package/assets/docs/help/faq/general-inquiry/what-is-the-small-business-fee.md +41 -0
  235. package/assets/docs/help/faq/payment-faq/business-card-available.md +55 -0
  236. package/assets/docs/help/faq/payment-faq/business-info-change.md +96 -0
  237. package/assets/docs/help/faq/payment-faq/cancel-deadline.md +32 -0
  238. package/assets/docs/help/faq/payment-faq/cancel-guide.md +48 -0
  239. package/assets/docs/help/faq/payment-faq/cash-receipt-guide.md +64 -0
  240. package/assets/docs/help/faq/payment-faq/content200022.md +73 -0
  241. package/assets/docs/help/faq/payment-faq/pg-max-min-amount.md +158 -0
  242. package/assets/docs/help/faq/payment-faq/receipt-guide.md +38 -0
  243. package/assets/docs/help/faq/payment-faq/tosspayments-cash-receipt.md +32 -0
  244. package/assets/docs/help/faq/payment-faq/vbank-info.md +35 -0
  245. package/assets/docs/help/faq/payment-faq/vbank-service-detail.md +67 -0
  246. package/assets/docs/help/faq/payment-faq/vbank-test-guide.md +65 -0
  247. package/assets/docs/help/faq/platform-faq/platform_function_partnerbulkupload.md +190 -0
  248. package/assets/docs/help/faq/platform-faq/platform_infosec.md +56 -0
  249. package/assets/docs/help/faq/platform-faq/platform_nocode_guide.md +104 -0
  250. package/assets/docs/help/faq/platform-faq/platform_payout_guide.md +75 -0
  251. package/assets/docs/help/faq/platform-faq/platform_policymanagement_calculator.md +73 -0
  252. package/assets/docs/help/faq/platform-faq/platform_settlmentandpayout_guide.md +83 -0
  253. package/assets/docs/help/faq/platform-faq/platform_tax_setting_guide.md +108 -0
  254. package/assets/docs/help/faq/platform-faq/taxinvoice_guide.md +107 -0
  255. package/assets/docs/help/faq/platform-faq/taxinvoice_signupemail.md +67 -0
  256. package/assets/docs/help/holidays.md +29 -0
  257. package/assets/docs/help/notice-kakaopay-2025-06-30.md +47 -0
  258. package/assets/docs/help/platform-2024-11-07.md +73 -0
  259. package/assets/docs/help/platform-2024-11-25.md +91 -0
  260. package/assets/docs/help/platform-2024-12-10.md +71 -0
  261. package/assets/docs/help/platform-2025-03-04.md +54 -0
  262. package/assets/docs/help/platform-2025-04-03.md +105 -0
  263. package/assets/docs/help/platform-2025-04-08.md +120 -0
  264. package/assets/docs/help/platform-2025-05-14.md +70 -0
  265. package/assets/docs/help/platform-2025-1-16.md +65 -0
  266. package/assets/docs/help/platform-2025-2-10.md +75 -0
  267. package/assets/docs/help/platform-2025-2-13.md +43 -0
  268. package/assets/docs/help/portone-service/portone-service/content50.md +80 -0
  269. package/assets/docs/help/pricing/pg/domestic-and-oversea-fees.md +34 -0
  270. package/assets/docs/help/pricing/portone/portone-pricing.md +116 -0
  271. package/assets/docs/help/procedure/payment-integration/real-integration.md +43 -0
  272. package/assets/docs/help/procedure/payment-integration/test-integration.md +91 -0
  273. package/assets/docs/help/procedure/pg-application/content53.md +49 -0
  274. package/assets/docs/help/procedure/pg-application/content54.md +136 -0
  275. package/assets/docs/help/procedure/pg-application/content59.md +53 -0
  276. package/assets/docs/help/procedure/pg-application/start.md +154 -0
  277. package/assets/docs/help/procedure/recommendation/package.md +98 -0
  278. package/assets/docs/help/procedure/service-addition/add-service.md +98 -0
  279. package/assets/docs/help/recon_issue1.md +25 -0
  280. package/assets/docs/help/recon_issue2.md +29 -0
  281. package/assets/docs/help/service/identity-verification/identity-verification.md +65 -0
  282. package/assets/docs/help/service/integration-type/integration-type.md +49 -0
  283. package/assets/docs/help/service/payment-type/payment-type.md +100 -0
  284. package/assets/docs/help/service/paymethod/paymethod.md +100 -0
  285. package/assets/docs/help/service/pg/pg-service-list.md +339 -0
  286. package/assets/docs/help/service/version/portone-version.md +61 -0
  287. package/assets/docs/opi/ko/console/guide/account.md +111 -0
  288. package/assets/docs/opi/ko/console/guide/api-keys.md +34 -0
  289. package/assets/docs/opi/ko/console/guide/billing-payments.md +312 -0
  290. package/assets/docs/opi/ko/console/guide/channel-manage.md +303 -0
  291. package/assets/docs/opi/ko/console/guide/connect.md +84 -0
  292. package/assets/docs/opi/ko/console/guide/home.md +37 -0
  293. package/assets/docs/opi/ko/console/guide/integration.md +18 -0
  294. package/assets/docs/opi/ko/console/guide/list.md +183 -0
  295. package/assets/docs/opi/ko/console/guide/pay.md +14 -0
  296. package/assets/docs/opi/ko/console/guide/payments.md +298 -0
  297. package/assets/docs/opi/ko/console/guide/promotion.md +140 -0
  298. package/assets/docs/opi/ko/console/guide/readme.md +22 -0
  299. package/assets/docs/opi/ko/console/guide/reg.md +159 -0
  300. package/assets/docs/opi/ko/console/guide/smartrouting.md +74 -0
  301. package/assets/docs/opi/ko/console/pg.md +92 -0
  302. package/assets/docs/opi/ko/etc/recon.md +286 -0
  303. package/assets/docs/opi/ko/extra/confirm-process/readme-v1.md +98 -0
  304. package/assets/docs/opi/ko/extra/confirm-process/readme-v2.md +152 -0
  305. package/assets/docs/opi/ko/extra/identity-verification/readme-v2.md +261 -0
  306. package/assets/docs/opi/ko/extra/identity-verification/v1/all/0.md +35 -0
  307. package/assets/docs/opi/ko/extra/identity-verification/v1/all/1.md +44 -0
  308. package/assets/docs/opi/ko/extra/identity-verification/v1/all/2.md +75 -0
  309. package/assets/docs/opi/ko/extra/identity-verification/v1/all/3.md +134 -0
  310. package/assets/docs/opi/ko/extra/identity-verification/v1/all/readme.md +17 -0
  311. package/assets/docs/opi/ko/extra/identity-verification/v1/credit-auth/1.md +35 -0
  312. package/assets/docs/opi/ko/extra/identity-verification/v1/credit-auth/2.md +78 -0
  313. package/assets/docs/opi/ko/extra/identity-verification/v1/credit-auth/3.md +75 -0
  314. package/assets/docs/opi/ko/extra/identity-verification/v1/credit-auth/4.md +134 -0
  315. package/assets/docs/opi/ko/extra/identity-verification/v1/credit-auth/readme.md +17 -0
  316. package/assets/docs/opi/ko/extra/identity-verification/v1/phone/1.md +37 -0
  317. package/assets/docs/opi/ko/extra/identity-verification/v1/phone/2.md +86 -0
  318. package/assets/docs/opi/ko/extra/identity-verification/v1/phone/3.md +77 -0
  319. package/assets/docs/opi/ko/extra/identity-verification/v1/phone/4.md +149 -0
  320. package/assets/docs/opi/ko/extra/identity-verification/v1/phone/readme.md +19 -0
  321. package/assets/docs/opi/ko/extra/identity-verification/v1/readme.md +9 -0
  322. package/assets/docs/opi/ko/extra/link-pay/readme-v1.md +235 -0
  323. package/assets/docs/opi/ko/extra/plugins/readme-v1.md +7 -0
  324. package/assets/docs/opi/ko/extra/plugins/shopify/readme-v1.md +54 -0
  325. package/assets/docs/opi/ko/extra/plugins/shopify/shopify/shopify-1.md +16 -0
  326. package/assets/docs/opi/ko/extra/plugins/shopify/shopify/shopify-2.md +32 -0
  327. package/assets/docs/opi/ko/extra/plugins/shopify/shopify/shopify-3.md +16 -0
  328. package/assets/docs/opi/ko/extra/plugins/shopify/shopify/shopify-4.md +35 -0
  329. package/assets/docs/opi/ko/extra/plugins/shopify/shopify/shopify-5.md +27 -0
  330. package/assets/docs/opi/ko/extra/plugins/wordpress/button.md +166 -0
  331. package/assets/docs/opi/ko/extra/plugins/wordpress/edd.md +56 -0
  332. package/assets/docs/opi/ko/extra/plugins/wordpress/readme-v1.md +10 -0
  333. package/assets/docs/opi/ko/extra/plugins/wordpress/woocommerce/payment.md +62 -0
  334. package/assets/docs/opi/ko/extra/plugins/wordpress/woocommerce/readme.md +30 -0
  335. package/assets/docs/opi/ko/extra/plugins/wordpress/woocommerce/subscription.md +60 -0
  336. package/assets/docs/opi/ko/extra/plugins/wordpress/woocommerce/vbank.md +64 -0
  337. package/assets/docs/opi/ko/extra/promotion/console-guide.md +148 -0
  338. package/assets/docs/opi/ko/extra/promotion/integration.md +769 -0
  339. package/assets/docs/opi/ko/extra/promotion/intro.md +31 -0
  340. package/assets/docs/opi/ko/extra/promotion/refund.md +316 -0
  341. package/assets/docs/opi/ko/extra/smart-routing/console-guide.md +73 -0
  342. package/assets/docs/opi/ko/extra/smart-routing/integration.md +1324 -0
  343. package/assets/docs/opi/ko/extra/smart-routing/intro.md +155 -0
  344. package/assets/docs/opi/ko/integration/cancel/v1/basic.md +324 -0
  345. package/assets/docs/opi/ko/integration/cancel/v1/virtual-account.md +99 -0
  346. package/assets/docs/opi/ko/integration/cancel/v2/readme.md +378 -0
  347. package/assets/docs/opi/ko/integration/checklist/readme-v1.md +83 -0
  348. package/assets/docs/opi/ko/integration/pg/v1/blue.md +99 -0
  349. package/assets/docs/opi/ko/integration/pg/v1/danal.md +409 -0
  350. package/assets/docs/opi/ko/integration/pg/v1/daou/readme.md +305 -0
  351. package/assets/docs/opi/ko/integration/pg/v1/daou/undefined.md +214 -0
  352. package/assets/docs/opi/ko/integration/pg/v1/eximbay.md +242 -0
  353. package/assets/docs/opi/ko/integration/pg/v1/hyphen.md +196 -0
  354. package/assets/docs/opi/ko/integration/pg/v1/inicis.md +444 -0
  355. package/assets/docs/opi/ko/integration/pg/v1/kakaopay.md +207 -0
  356. package/assets/docs/opi/ko/integration/pg/v1/kcp-v2-identity-verification.md +188 -0
  357. package/assets/docs/opi/ko/integration/pg/v1/kg.md +177 -0
  358. package/assets/docs/opi/ko/integration/pg/v1/kicc.md +308 -0
  359. package/assets/docs/opi/ko/integration/pg/v1/ksnet/readme.md +533 -0
  360. package/assets/docs/opi/ko/integration/pg/v1/ksnet/warning.md +175 -0
  361. package/assets/docs/opi/ko/integration/pg/v1/naver.md +558 -0
  362. package/assets/docs/opi/ko/integration/pg/v1/newtoss/readme.md +503 -0
  363. package/assets/docs/opi/ko/integration/pg/v1/newtoss/warning.md +364 -0
  364. package/assets/docs/opi/ko/integration/pg/v1/nhn-kcp.md +1186 -0
  365. package/assets/docs/opi/ko/integration/pg/v1/nice-v2/billing-api-caution.md +150 -0
  366. package/assets/docs/opi/ko/integration/pg/v1/nice-v2/payment-caution.md +429 -0
  367. package/assets/docs/opi/ko/integration/pg/v1/nice-v2/readme.md +364 -0
  368. package/assets/docs/opi/ko/integration/pg/v1/nice.md +319 -0
  369. package/assets/docs/opi/ko/integration/pg/v1/payco.md +201 -0
  370. package/assets/docs/opi/ko/integration/pg/v1/paymentwall.md +283 -0
  371. package/assets/docs/opi/ko/integration/pg/v1/paypal.md +158 -0
  372. package/assets/docs/opi/ko/integration/pg/v1/readme.md +73 -0
  373. package/assets/docs/opi/ko/integration/pg/v1/rt/readme.md +501 -0
  374. package/assets/docs/opi/ko/integration/pg/v1/rt/warning.md +297 -0
  375. package/assets/docs/opi/ko/integration/pg/v1/settle/mybank.md +304 -0
  376. package/assets/docs/opi/ko/integration/pg/v1/settle/readme.md +185 -0
  377. package/assets/docs/opi/ko/integration/pg/v1/smartro-v2/api.md +197 -0
  378. package/assets/docs/opi/ko/integration/pg/v1/smartro-v2/caution.md +387 -0
  379. package/assets/docs/opi/ko/integration/pg/v1/smartro-v2/readme.md +293 -0
  380. package/assets/docs/opi/ko/integration/pg/v1/smartro.md +104 -0
  381. package/assets/docs/opi/ko/integration/pg/v1/smilepay.md +97 -0
  382. package/assets/docs/opi/ko/integration/pg/v1/spb/readme.md +441 -0
  383. package/assets/docs/opi/ko/integration/pg/v1/spb/stc.md +226 -0
  384. package/assets/docs/opi/ko/integration/pg/v1/spb/warning.md +729 -0
  385. package/assets/docs/opi/ko/integration/pg/v1/toss-brandpay/module.md +227 -0
  386. package/assets/docs/opi/ko/integration/pg/v1/toss-brandpay/readme.md +404 -0
  387. package/assets/docs/opi/ko/integration/pg/v1/toss-brandpay/warning.md +81 -0
  388. package/assets/docs/opi/ko/integration/pg/v1/toss-brandpay/widget.md +152 -0
  389. package/assets/docs/opi/ko/integration/pg/v1/toss.md +112 -0
  390. package/assets/docs/opi/ko/integration/pg/v1/tosspay-v2/caution.md +65 -0
  391. package/assets/docs/opi/ko/integration/pg/v1/tosspay-v2/readme.md +308 -0
  392. package/assets/docs/opi/ko/integration/pg/v1/tosspay.md +110 -0
  393. package/assets/docs/opi/ko/integration/pg/v1/welcome/api.md +229 -0
  394. package/assets/docs/opi/ko/integration/pg/v1/welcome/caution.md +517 -0
  395. package/assets/docs/opi/ko/integration/pg/v1/welcome/readme.md +795 -0
  396. package/assets/docs/opi/ko/integration/pg/v2/danal-identity-verification.md +121 -0
  397. package/assets/docs/opi/ko/integration/pg/v2/eximbay-v2.md +964 -0
  398. package/assets/docs/opi/ko/integration/pg/v2/hyphen.md +205 -0
  399. package/assets/docs/opi/ko/integration/pg/v2/inicis-jp.md +403 -0
  400. package/assets/docs/opi/ko/integration/pg/v2/inicis-unified-identity-verification.md +89 -0
  401. package/assets/docs/opi/ko/integration/pg/v2/inicis-v2.md +1598 -0
  402. package/assets/docs/opi/ko/integration/pg/v2/kakaopay.md +108 -0
  403. package/assets/docs/opi/ko/integration/pg/v2/kcp-v2-identity-verification.md +291 -0
  404. package/assets/docs/opi/ko/integration/pg/v2/kcp-v2.md +1273 -0
  405. package/assets/docs/opi/ko/integration/pg/v2/kpn.md +1133 -0
  406. package/assets/docs/opi/ko/integration/pg/v2/ksnet.md +526 -0
  407. package/assets/docs/opi/ko/integration/pg/v2/naverpay.md +186 -0
  408. package/assets/docs/opi/ko/integration/pg/v2/nice-v2.md +868 -0
  409. package/assets/docs/opi/ko/integration/pg/v2/payletter-global.md +62 -0
  410. package/assets/docs/opi/ko/integration/pg/v2/paypal-v2.md +1874 -0
  411. package/assets/docs/opi/ko/integration/pg/v2/readme.md +194 -0
  412. package/assets/docs/opi/ko/integration/pg/v2/smartro-v2.md +1043 -0
  413. package/assets/docs/opi/ko/integration/pg/v2/tosspay-v2.md +433 -0
  414. package/assets/docs/opi/ko/integration/pg/v2/tosspayments.md +551 -0
  415. package/assets/docs/opi/ko/integration/pg/v2/welcome.md +1441 -0
  416. package/assets/docs/opi/ko/integration/ready/readme.md +1069 -0
  417. package/assets/docs/opi/ko/integration/start/v1/auth.md +483 -0
  418. package/assets/docs/opi/ko/integration/start/v1/non-auth.md +420 -0
  419. package/assets/docs/opi/ko/integration/start/v1/readme.md +9 -0
  420. package/assets/docs/opi/ko/integration/start/v2/billing/issue.md +122 -0
  421. package/assets/docs/opi/ko/integration/start/v2/billing/payment.md +41 -0
  422. package/assets/docs/opi/ko/integration/start/v2/billing/readme.md +24 -0
  423. package/assets/docs/opi/ko/integration/start/v2/billing/schedule.md +54 -0
  424. package/assets/docs/opi/ko/integration/start/v2/checkout.md +258 -0
  425. package/assets/docs/opi/ko/integration/start/v2/keyin.md +54 -0
  426. package/assets/docs/opi/ko/integration/start/v2/readme.md +120 -0
  427. package/assets/docs/opi/ko/integration/using-ai-tools.md +140 -0
  428. package/assets/docs/opi/ko/integration/virtual-account/readme.md +268 -0
  429. package/assets/docs/opi/ko/integration/webhook/readme-v1.md +242 -0
  430. package/assets/docs/opi/ko/integration/webhook/readme-v2.md +599 -0
  431. package/assets/docs/opi/ko/quick-guide/payment.md +3305 -0
  432. package/assets/docs/opi/ko/readme.md +193 -0
  433. package/assets/docs/opi/ko/support/agency-and-tier.md +51 -0
  434. package/assets/docs/opi/ko/support/code-info/card-code.md +29 -0
  435. package/assets/docs/opi/ko/support/code-info/code.md +22 -0
  436. package/assets/docs/opi/ko/support/code-info/pg-1.md +161 -0
  437. package/assets/docs/opi/ko/support/code-info/pg-2.md +41 -0
  438. package/assets/docs/opi/ko/support/code-info/pg-3.md +36 -0
  439. package/assets/docs/opi/ko/support/code-info/pg.md +41 -0
  440. package/assets/docs/opi/ko/support/code-info/portone-code.md +15 -0
  441. package/assets/docs/opi/ko/support/code-info/readme.md +20 -0
  442. package/assets/docs/opi/ko/support/contact.md +23 -0
  443. package/assets/docs/opi/ko/support/faq/undefined.md +60 -0
  444. package/assets/docs/opi/ko/support/flow.md +24 -0
  445. package/assets/docs/opi/ko/support/redirect.md +26 -0
  446. package/assets/docs/opi/ko/support/tax.md +142 -0
  447. package/assets/docs/opi/ko/support/tls-support.md +497 -0
  448. package/assets/docs/platform/ko/guides/dictionary.md +20 -0
  449. package/assets/docs/platform/ko/guides/process.md +33 -0
  450. package/assets/docs/platform/ko/guides/webhook.md +171 -0
  451. package/assets/docs/platform/ko/readme.md +64 -0
  452. package/assets/docs/platform/ko/usages/client.md +17 -0
  453. package/assets/docs/platform/ko/usages/contract.md +126 -0
  454. package/assets/docs/platform/ko/usages/discount.md +89 -0
  455. package/assets/docs/platform/ko/usages/fee.md +97 -0
  456. package/assets/docs/platform/ko/usages/order.md +1666 -0
  457. package/assets/docs/platform/ko/usages/partner.md +115 -0
  458. package/assets/docs/platform/ko/using-ai-tools.md +140 -0
  459. package/assets/docs/release-notes/api-sdk/2023-04-24.md +10 -0
  460. package/assets/docs/release-notes/api-sdk/2023-05-08.md +22 -0
  461. package/assets/docs/release-notes/api-sdk/2023-05-12.md +24 -0
  462. package/assets/docs/release-notes/api-sdk/2023-05-22.md +46 -0
  463. package/assets/docs/release-notes/api-sdk/2023-05-25.md +15 -0
  464. package/assets/docs/release-notes/api-sdk/2023-06-19.md +20 -0
  465. package/assets/docs/release-notes/api-sdk/2023-07-03.md +26 -0
  466. package/assets/docs/release-notes/api-sdk/2023-07-10.md +35 -0
  467. package/assets/docs/release-notes/api-sdk/2023-07-17.md +31 -0
  468. package/assets/docs/release-notes/api-sdk/2023-07-31.md +14 -0
  469. package/assets/docs/release-notes/api-sdk/2023-08-31.md +29 -0
  470. package/assets/docs/release-notes/api-sdk/2023-12-15.md +12 -0
  471. package/assets/docs/release-notes/api-sdk/2024-01-08.md +36 -0
  472. package/assets/docs/release-notes/api-sdk/2024-01-11.md +18 -0
  473. package/assets/docs/release-notes/api-sdk/2024-01-25.md +35 -0
  474. package/assets/docs/release-notes/api-sdk/2024-01-30.md +19 -0
  475. package/assets/docs/release-notes/api-sdk/2024-02-23.md +26 -0
  476. package/assets/docs/release-notes/api-sdk/2024-03-08.md +12 -0
  477. package/assets/docs/release-notes/api-sdk/2024-03-15.md +30 -0
  478. package/assets/docs/release-notes/api-sdk/2024-03-25.md +23 -0
  479. package/assets/docs/release-notes/api-sdk/2024-04-01.md +37 -0
  480. package/assets/docs/release-notes/api-sdk/2024-04-05.md +10 -0
  481. package/assets/docs/release-notes/api-sdk/2024-04-12.md +32 -0
  482. package/assets/docs/release-notes/api-sdk/2024-05-08.md +34 -0
  483. package/assets/docs/release-notes/api-sdk/2024-05-14.md +17 -0
  484. package/assets/docs/release-notes/api-sdk/2024-06-03.md +48 -0
  485. package/assets/docs/release-notes/api-sdk/2024-06-17.md +37 -0
  486. package/assets/docs/release-notes/api-sdk/2024-06-19.md +39 -0
  487. package/assets/docs/release-notes/api-sdk/2024-06-20.md +13 -0
  488. package/assets/docs/release-notes/api-sdk/2024-07-04.md +15 -0
  489. package/assets/docs/release-notes/api-sdk/2024-07-11.md +47 -0
  490. package/assets/docs/release-notes/api-sdk/2024-09-05.md +36 -0
  491. package/assets/docs/release-notes/api-sdk/2024-09-27.md +27 -0
  492. package/assets/docs/release-notes/api-sdk/2024-12-19.md +52 -0
  493. package/assets/docs/release-notes/api-sdk/2025-01-15.md +76 -0
  494. package/assets/docs/release-notes/api-sdk/2025-05-19.md +49 -0
  495. package/assets/docs/release-notes/console/2023-04-24.md +41 -0
  496. package/assets/docs/release-notes/console/2023-05-08.md +6 -0
  497. package/assets/docs/release-notes/console/2023-05-22.md +15 -0
  498. package/assets/docs/release-notes/console/2023-07-05.md +29 -0
  499. package/assets/docs/release-notes/console/2023-07-07.md +25 -0
  500. package/assets/docs/release-notes/console/2023-07-17.md +16 -0
  501. package/assets/docs/release-notes/console/2023-08-23.md +17 -0
  502. package/assets/docs/release-notes/console/2023-09-12.md +16 -0
  503. package/assets/docs/release-notes/console/2023-12-27.md +24 -0
  504. package/assets/docs/release-notes/console/2024-09-05.md +23 -0
  505. package/assets/docs/release-notes/platform/2023-08-31.md +16 -0
  506. package/assets/docs/release-notes/platform/2023-11-08.md +37 -0
  507. package/assets/docs/release-notes/platform/2023-11-14.md +28 -0
  508. package/assets/docs/release-notes/platform/2023-12-15.md +11 -0
  509. package/assets/docs/release-notes/platform/2024-05-20.md +31 -0
  510. package/assets/docs/release-notes/platform/2024-05-29.md +159 -0
  511. package/assets/docs/release-notes/platform/2024-06-05.md +30 -0
  512. package/assets/docs/release-notes/platform/2024-07-05.md +49 -0
  513. package/assets/docs/release-notes/platform/2024-07-11.md +62 -0
  514. package/assets/docs/release-notes/platform/2024-07-30.md +44 -0
  515. package/assets/docs/release-notes/platform/2024-08-07.md +37 -0
  516. package/assets/docs/release-notes/platform/2024-08-22.md +111 -0
  517. package/assets/docs/release-notes/platform/2024-10-21.md +75 -0
  518. package/assets/docs/release-notes/platform/2024-11-07.md +65 -0
  519. package/assets/docs/release-notes/platform/2024-11-25.md +82 -0
  520. package/assets/docs/release-notes/platform/2024-12-10.md +61 -0
  521. package/assets/docs/release-notes/platform/2025-01-16.md +56 -0
  522. package/assets/docs/release-notes/platform/2025-02-10.md +67 -0
  523. package/assets/docs/release-notes/platform/2025-02-13.md +27 -0
  524. package/assets/docs/release-notes/platform/2025-03-04.md +49 -0
  525. package/assets/docs/release-notes/platform/2025-03-17.md +40 -0
  526. package/assets/docs/release-notes/platform/2025-04-03.md +96 -0
  527. package/assets/docs/release-notes/platform/2025-04-08.md +119 -0
  528. package/assets/docs/release-notes/platform/2025-05-14.md +77 -0
  529. package/assets/docs/release-notes/platform/2025-07-02.md +64 -0
  530. package/assets/docs/release-notes/platform/2025-07-07.md +55 -0
  531. package/assets/docs/schema/browser-sdk.yml +6349 -0
  532. package/assets/docs/schema/v1.openapi.json +11632 -0
  533. package/assets/docs/schema/v1.openapi.yml +11489 -0
  534. package/assets/docs/schema/v2.graphql +13849 -0
  535. package/assets/docs/schema/v2.openapi.json +47721 -0
  536. package/assets/docs/schema/v2.openapi.yml +34749 -0
  537. package/assets/docs/sdk/ko/readme.md +56 -0
  538. package/assets/docs/sdk/ko/v1-mobile-sdk/readme.md +31 -0
  539. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/cft-rt.md +32 -0
  540. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/cft.md +108 -0
  541. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/load-module-rt.md +10 -0
  542. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/load-module.md +55 -0
  543. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/payrq.md +502 -0
  544. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/payrt.md +300 -0
  545. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk/readme.md +46 -0
  546. package/assets/docs/sdk/ko/v1-sdk/javascript-sdk-old/readme.md +33 -0
  547. package/assets/docs/sdk/ko/v2-mobile-sdk/android.md +268 -0
  548. package/assets/docs/sdk/ko/v2-mobile-sdk/react-native.md +239 -0
  549. package/assets/docs/sdk/ko/v2-mobile-sdk/readme.md +30 -0
  550. package/assets/docs/sdk/ko/v2-sdk/billing-key-and-pay-request.md +26 -0
  551. package/assets/docs/sdk/ko/v2-sdk/billing-key-and-pay-response.md +15 -0
  552. package/assets/docs/sdk/ko/v2-sdk/billing-key-request.md +24 -0
  553. package/assets/docs/sdk/ko/v2-sdk/billing-key-response.md +15 -0
  554. package/assets/docs/sdk/ko/v2-sdk/changelog.md +8 -0
  555. package/assets/docs/sdk/ko/v2-sdk/identity-verification-request.md +15 -0
  556. package/assets/docs/sdk/ko/v2-sdk/identity-verification-response.md +15 -0
  557. package/assets/docs/sdk/ko/v2-sdk/load-issue-billing-key-ui-request.md +15 -0
  558. package/assets/docs/sdk/ko/v2-sdk/load-issue-billing-key-ui-response.md +23 -0
  559. package/assets/docs/sdk/ko/v2-sdk/load-payment-ui-request.md +15 -0
  560. package/assets/docs/sdk/ko/v2-sdk/load-payment-ui-response.md +23 -0
  561. package/assets/docs/sdk/ko/v2-sdk/payment-request.md +15 -0
  562. package/assets/docs/sdk/ko/v2-sdk/payment-response.md +15 -0
  563. package/assets/docs/sdk/ko/v2-sdk/readme.md +73 -0
  564. package/assets/docs/sdk/ko/v2-sdk/update-load-issue-billing-key-ui-request.md +15 -0
  565. package/assets/docs/sdk/ko/v2-sdk/update-load-payment-ui-request.md +15 -0
  566. package/assets/docs/sdk/ko/v2-server-sdk/javascript.md +170 -0
  567. package/assets/docs/sdk/ko/v2-server-sdk/jvm.md +170 -0
  568. package/assets/docs/sdk/ko/v2-server-sdk/python.md +152 -0
  569. package/assets/docs/sdk/ko/v2-server-sdk/readme.md +259 -0
  570. package/assets/docs/v1-docs-full.md +36540 -0
  571. package/assets/docs/v2-docs-full.md +34980 -0
  572. package/assets/docs/website-links.md +331 -0
  573. package/assets/instructions.md +19 -0
  574. package/manifest.json +50 -0
  575. package/package.json +66 -0
  576. package/server/index.js +1764 -0
@@ -0,0 +1,341 @@
1
+ ---
2
+ title: 포트원 V2 이야기 - OOM 이슈 탐방기
3
+ description: ZIO를 사용하면서 발생했던 OOM 이슈에 대해 원인을 분석합니다.
4
+ author: BaekGeunYoung
5
+ date: 2024-02-28T15:00:01.000Z
6
+ tags:
7
+ - Core V2
8
+ - Backend
9
+ - Scala
10
+ - ZIO
11
+ ---
12
+
13
+ 본 글은 ZIO에 대한 기본적인 이해를 전제로 하고 있으니, ZIO에 대한 간략한 소개는 [포트원 V2 이야기 -
14
+ Scala와 ZIO로 안정적인 결제 시스템 만들기](../v2-zio)를 참고해주세요.
15
+
16
+ ## 사전 배경: CQRS 패턴의 도입
17
+
18
+ 포트원 V2 모듈은 CQRS 패턴을 활용하고 있습니다. 앞선 [글](../v2-zio)에서 소개드렸듯이 포트원 V2는
19
+ 이벤트 소싱이라는 디자인 패턴을 도입했는데, 이벤트 소싱을 하게 되면 데이터가 훨씬 상세하게 남는다는
20
+ 장점이 있지만 상태가 아닌 개별 이벤트들이 DB에 저장되므로 다양한 필터를 통한 조회 요구사항에는
21
+ 적절하지 않습니다. 하지만 포트원 콘솔에는 다양한 필터 조건을 걸고, 정렬을 하며, 심지어는 full-text
22
+ search 까지 지원해야 하는 등의 고도화된 조회 요구사항이 존재합니다. 이러한 요구사항을 만족하기 위해
23
+ Write DB와 Read DB를 분리해서 사용하기로 결정했는데, 이러한 데이터 접근 패턴을 CQRS 패턴이라고
24
+ 부릅니다.
25
+
26
+ ## 문제 상황
27
+
28
+ CQRS 패턴을 구현하기 위해서는 Write DB에서 발생한 변경 사항을 Read DB에도 반영시켜주는 파이프라인이
29
+ 필요한데, Read DB를 통해 포트원에서 발생한 모든 결제건을 누락없이 볼 수 있어야 하므로 이
30
+ 파이프라인의 신뢰성을 높이는 것은 매우 중요합니다. 포트원 V2의 코어 모듈은 이 파이프라인의
31
+ 시작점이라고 할 수 있는데, Write DB에 발생한 변경사항을 주기적으로 캐치하여 Kafka로 publish를 해주는
32
+ background job이 존재합니다.
33
+
34
+ (관련 이미지 첨부)
35
+
36
+ 이러한 job은 recursive하게 자기 자신을 주기적으로 호출하는 방식으로 작성되어 있었는데, 대략 아래와 같은 코드로 단순하게 표현할 수 있을 것입니다.
37
+
38
+ ```scala
39
+ private def scrapAndPublish: UIO[Nothing] =
40
+ for {
41
+ events <- getEvents
42
+ _ <- publish(events)
43
+ nothing <- scrapAndPublish.delay(1.second)
44
+ } yield nothing
45
+ ```
46
+
47
+ 해당 코드를 배포하고 난 직후엔 아무 문제 없이 의도한대로 잘 동작하였으나, 코드를 배포한 지 며칠이
48
+ 지나자 OOM이 발생해 어플리케이션이 강제로 재시작되었고 다시 며칠동안은 문제가 없다가 OOM이
49
+ 발생하기를 반복했습니다. 아래의 두 사진 중 위의 것은 문제가 된 인스턴스의 JVM old generation size
50
+ 추이를 나타낸 그래프이고, 아래의 사진은 같은 메트릭에 대해 문제가 없는 인스턴스의 일반적인
51
+ 그래프입니다.
52
+
53
+ (관련 이미지 첨부)
54
+
55
+ (관련 이미지 첨부)
56
+
57
+ 그래프를 통해 계속적인 재귀 호출로 인해 메모리가 정리되지 않고 계속해서 쌓이고 있음을 짐작할 수
58
+ 있었고, 이 이슈를 해결하기 위해 몇 가지 실험과 ZIO의 코드 분석을 진행했습니다.
59
+
60
+ ## 무한 재귀인데 OOM이 나는 이유
61
+
62
+ 그런데 무언가 이상했습니다. 상황을 확인하고 가장 먼저 든 생각은 “무한 재귀 호출로 인해 문제가
63
+ 생긴거라면 `OutOfMemoryError`가 아니라 `StackOverflowError`가 발생해야 하는 것 아닌가?”하는
64
+ 것이었습니다. 일반적으로 함수의 호출은 스택이라는 메모리 공간에 스택 프레임을 생성하기 때문에 함수
65
+ 호출의 깊이가 매우 깊어지면 스택에 남은 공간이 없어질 것이기 때문이죠. 예를 들어 아래와 같은
66
+ `Option`을 이용한 무한 재귀 호출 코드는 `StackOverflowError`를 발생시킬 것입니다.
67
+
68
+ ```scala
69
+ def optionInfRec(n: Int): Option[Nothing] =
70
+ Some(n).flatMap(_ => optionInfRec(n + 1))
71
+
72
+ optionInfRec(0)
73
+
74
+ --- 실행결과 ---
75
+ java.lang.StackOverflowError
76
+ at scala.Option.flatMap(Option.scala:283)
77
+ at io.portone.tx.InfRecSpec$.optionInfRec$1(InfRecSpec.scala:11)
78
+ at io.portone.tx.InfRecSpec$.$anonfun$spec$2(InfRecSpec.scala:12)
79
+ at io.portone.tx.InfRecSpec$.$anonfun$spec$2$adapted(InfRecSpec.scala:11)
80
+ at scala.Option.flatMap(Option.scala:283)
81
+ at io.portone.tx.InfRecSpec$.optionInfRec$1(InfRecSpec.scala:11)
82
+ at io.portone.tx.InfRecSpec$.$anonfun$spec$2(InfRecSpec.scala:12)
83
+ at io.portone.tx.InfRecSpec$.$anonfun$spec$2$adapted(InfRecSpec.scala:11)
84
+ ```
85
+
86
+ 하지만 ZIO를 이용한 무한 재귀 호출 코드는 `StackOverflowError`를 발생시키지 않습니다.
87
+
88
+ ```scala
89
+ def zioInfRec(n: Int): UIO[Nothing] =
90
+ ZIO.succeed(n).flatMap(_ => zioInfRec(n + 1))
91
+
92
+ unsafeRun(zioInfRec(0))
93
+ ```
94
+
95
+ ### ZIO의 Lazy Evaluation
96
+
97
+ ZIO의 경우에만 `StackOverflowError` 가 발생하지 않는 이유는 우선 ZIO는 `flatMap`의 인자로 전달 받은 함수를 즉시 평가하지 않기 때문입니다.
98
+ 지난 글에서 ZIO는 동작을 기술하는 data type일 뿐이라고 소개했던 것을 기억하시나요?
99
+ ZIO에서 제공하는 `flatMap`이라는 함수에서는 파라미터로 받은 `k` 함수를 평가하지 않고 `OnSuccess`라는 data type에 그대로 넣어주고 있습니다.
100
+
101
+ ```scala
102
+ def flatMap[R1 <: R, E1 >: E, B](k: A => ZIO[R1, E1, B])(implicit trace: Trace): ZIO[R1, E1, B] =
103
+ ZIO.OnSuccess(trace, self, k) // OnSuccess는 ZIO의 subtype입니다.
104
+ ```
105
+
106
+ 반면 Option의 `flatMap` 구현을 보면 파라미터로 받은 f 함수를 즉시 평가하고 있습니다.
107
+
108
+ ```scala
109
+ @inline final def flatMap[B](f: A => Option[B]): Option[B] =
110
+ if (isEmpty) None else f(this.get)
111
+ ```
112
+
113
+ 다시 말해 위의 무한 재귀 호출 코드에서 재귀 호출이 일어나는 부분은 lambda 함수로 감싸져 있는데,
114
+ ZIO는 이 함수를 즉시 평가하지 않기 때문에 이 코드는 실제로는 재귀 호출을 하지 않는 코드가 되는
115
+ 것이죠! 이를 확인하기 위해 우리가 만든 ZIO를 한번 콘솔에 출력해보겠습니다.
116
+
117
+ ```scala
118
+ OnSuccess(
119
+ io.portone.tx.InfRecSpec.zioInfRec(InfRecSpec.scala:10),
120
+ Sync(
121
+ io.portone.tx.InfRecSpec.zioInfRec(InfRecSpec.scala:10),
122
+ io.portone.tx.InfRecSpec$$$Lambda$1000/0x00000008012be5e8@398d44c6
123
+ ),
124
+ io.portone.tx.InfRecSpec$$$Lambda$1001/0x00000008012be8a8@781006f6
125
+ )
126
+ ```
127
+
128
+ 실제로 `OnSuccess`가 계속해서 nested된 형태가 아니라, lambda 함수의 reference만 들어있는 것을 확인할 수 있습니다.
129
+
130
+ ## ZIO가 실제로 실행되는 원리
131
+
132
+ 여기까지 확인을 했다면, **“이건 단순히 평가 시점을 뒤로 미룬 것 뿐이고, 나중에 lambda가 실제로
133
+ 평가될 때는 결국 문제가 발생하는거 아닌가?”** 하는 생각이 들 수 있습니다. 이 부분에 대한 궁금증을
134
+ 해소하기 위해서는 우선 단순한 데이터 타입에 불과한 ZIO가 어떻게 실제로 실행되는지에 대해 이해해야
135
+ 하는데요, 이 때 알아야 하는 개념이 `Fiber`와 `FiberRuntime`입니다.
136
+
137
+ ### Fiber와 FiberRuntime
138
+
139
+ ZIO는 `Fiber`라는 객체 위에서 실행되고 그 실행 환경을 구체적으로 정의한 구현체를 `FiberRuntime`이라고 부릅니다.
140
+
141
+ ```scala
142
+ // 실행하고자 하는 zio 객체를 이용해 Fiber를 만듭니다.
143
+ val fiber = makeFiber(zio)
144
+ // Fiber.start 함수를 호출하면 zio가 실제로 평가됩니다.
145
+ fiber.start(zio)
146
+ ```
147
+
148
+ `FiberRuntime` 에는 실제로 ZIO를 한 단계씩 실행해나가는 `runLoop` 라는 함수가 존재하는데, 이 함수는
149
+ ZIO를 바깥에서부터 하나씩 처리하고 다음 평가할 ZIO를 만들어 이어서 처리하는 식으로 구현되어
150
+ 있습니다. 위에서 소개한 `OnSuccess` 객체를 처리하는 로직은 어떻게 작성되어 있을까요? `OnSuccess` 는
151
+ 순차 실행을 추상화한 데이터 타입이며, 그렇기 때문에 가장 기본적이고도 가장 강력한 Effect 조합기라고
152
+ 할 수 있습니다. 이를 처리하는 코드를 직접 살펴보겠습니다. (편의를 위해 코드를 단순화했으며, 실제
153
+ ZIO의 코드와는 상이한 부분이 있을 수 있습니다.)
154
+
155
+ ```scala
156
+ private def runLoop(effect: ZIO[Any, Any, Any], currentDepth: Int): AnyRef = {
157
+ ...
158
+
159
+ effect match {
160
+ case onSuccess: OnSuccess[_, _, _, _] =>
161
+ val first = onSuccess.first
162
+ val andThen = onSuccess.successK
163
+
164
+ // 첫번째 effect를 runLoop 함수를 재귀호출하여 평가하고,
165
+ // andThen 함수에 이 결과를 넣어 이어서 평가할 ZIO 객체를 만듭니다.
166
+ val continuation = andThen(runLoop(first, currentDepth + 1))
167
+ ...
168
+
169
+ case ... // handling other cases
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Stack Safety를 위한 Trampoline
175
+
176
+ 위 코드에서 하는 일은 첫번째 ZIO를 `runLoop` 함수를 재귀호출하여 먼저 평가하고, 그 결과를 가지고
177
+ `andThen` 함수를 호출해 이어서 평가할 ZIO 객체를 만드는 것입니다. 어쨌거나 `runLoop` 함수를 재귀
178
+ 호출하기 때문에 원래 가졌던 궁금증처럼 여기서도 결국 `StackOverflowError`가 발생할 가능성이
179
+ 있어보입니다. 하지만 그럼에도 실제로는 `StackOverflowError`가 발생하지 않았던 이유는 무엇일까요? 그
180
+ 이유는 함수 호출의 깊이가 특정 임계값에 도달하면 즉시 스택을 비우고 연산을 한번 중지했다가 다시
181
+ 시작하는 로직이 존재하기 때문입니다. `runLoop` 함수의 파라미터로 `currentDepth` 가 존재하는 것이
182
+ 보이시나요? 재귀 호출을 할 때마다 이 값을 하나씩 늘려 호출을 하고, 함수의 첫 시작 부분에는 이 값이
183
+ 임계치에 도달했는지를 확인하는 로직이 존재합니다.
184
+
185
+ ```scala
186
+ if (currentDepth >= FiberRuntime.MaxDepthBeforeTrampoline) {
187
+ ...
188
+
189
+ throw Trampoline(effect, false)
190
+ }
191
+ ```
192
+
193
+ 여기서 발생한 `Trampoline` 이라는 에러를 catch하는 로직은 아래와 같습니다.
194
+
195
+ ```scala
196
+ ...
197
+ catch {
198
+ case trampoline: Trampoline =>
199
+ tell(FiberMessage.Resume(trampoline.effect))
200
+ }
201
+ ```
202
+
203
+ 이렇게 연산을 실제로 이어나가는 대신, “연산을 재개해라”라는 명령을 추상화한 객체를 만듦으로써
204
+ 명시적으로 스택을 비워 stack safety를 달성할 수 있습니다. Exception의 이름이 `Trampoline` 이라는
205
+ 것이 조금 특이한데, 사실 Trampoline이란 이렇게 **함수의 stack safety를 보장하기 위해 stack 대신
206
+ heap을 사용해 연산을 이어나가는 일반화된 패턴**을 의미하는 용어입니다. Trampoline은 stack safety를
207
+ 보장할 수 있는 우아한 함수형 기법이지만, heap을 이용하기 때문에 메모리 동적 할당에 따른 오버헤드를
208
+ 감수해야 하고 메모리에 random access를 해야 하기 때문에 stack을 일반적으로 이용하는 것보다
209
+ locality가 떨어져 cache 효율성이 낮아질 수 있습니다.
210
+
211
+ ## 이제 진짜 원인 파악을 해보자
212
+
213
+ 여기까지 이해를 했다면, 원래의 코드에서 왜 문제가 생겼는지를 쉽게 이해할 수 있습니다. 사실 `runLoop`
214
+ 함수에서 하는 일이 한 가지 더 있는데, 함수 호출의 깊이가 깊어져 trampoline이 발생하게 되면 현재
215
+ effect를 나중에 평가하기 위해 stack에 쌓아두는 것입니다. 이 작업을 추가한 `runLoop` 코드는 다음과
216
+ 같습니다.
217
+
218
+ ```scala
219
+ private def runLoop(effect: ZIO[Any, Any, Any], currentDepth: Int): AnyRef = {
220
+ ...
221
+
222
+ effect match {
223
+ case onSuccess: OnSuccess[_, _, _, _] =>
224
+ val first = onSuccess.first
225
+ val andThen = onSuccess.successK
226
+
227
+ // 첫번째 effect를 runLoop 함수를 재귀호출하여 평가하고,
228
+ // andThen 함수에 이 결과를 넣어 이어서 평가할 ZIO 객체를 만듭니다.
229
+ try {
230
+ val continuation = andThen(runLoop(first, currentDepth + 1))
231
+ } catch {
232
+ case trampoline: Trampoline =>
233
+ // trampoline이 발생하면 나중에 effect를 다시 실행하기 위해 stack에 저장합니다.
234
+ self.reifiedStack += effect
235
+
236
+ throw trampoline
237
+ }
238
+
239
+ ...
240
+
241
+ case ... // handling other cases
242
+ }
243
+ }
244
+ ```
245
+
246
+ 우리의 코드는 trampoline이 무한히 발생해 stack에도 effect들이 무한히 쌓이게 되므로 OOM이 발생하는 것이었습니다!
247
+
248
+ (관련 이미지 첨부)
249
+
250
+ ## 해결 방법
251
+
252
+ 원인을 파악하기까지 꽤나 힘들었지만, 해결 방법은 단순합니다. 그것은 Scala의 for-comprehension을
253
+ 사용하지 않는 것인데요, for-comprehension으로 만들어진 코드는 우리가 정확히 원하는 코드와는 약간의
254
+ 차이가 있습니다. 윗부분에서 소개한 `zioInfRec` 함수를 for-comprehension 스타일로 바꾼다면 아래와
255
+ 같이 작성할 수 있을 것입니다.
256
+
257
+ ```scala
258
+ def withForComprehension(n: Int): UIO[Nothing] =
259
+ for {
260
+ _ <- ZIO.succeed(n)
261
+ nothing <- withForComprehension(n + 1)
262
+ } yield nothing
263
+ ```
264
+
265
+ 그리고 이 코드는 아래의 코드와 동치입니다.
266
+
267
+ ```scala
268
+ def withForComprehension2(n: Int): UIO[Nothing] =
269
+ ZIO
270
+ .succeed(n)
271
+ .flatMap(_ => withForComprehension2(n + 1).map(nothing => nothing))
272
+ ```
273
+
274
+ 가장 마지막 부분에 `.map(nothing => nothing)` 이 붙는 것이 보이시나요? 이 부분은 사실 없어도 되는
275
+ 코드이지만, for-comprehension의 변환 규칙에 의해 어쩔 수 없이 붙게 되는 코드입니다. 의미적으로는
276
+ 사실상 같은 일을 하는 코드이지만, 만들어지는 ZIO의 구성 자체는 분명한 차이점을 가지고 있으며 이를
277
+ `FiberRuntime` 위에서 실행할 때도 동작 방식에서 큰 차이를 갖게 됩니다.
278
+
279
+ ### for-comprehension 사용 유무에 따른 구조적 차이
280
+
281
+ 마지막 부분에 `.map(nothing => nothing)` 이 추가된 경우와 그렇지 않은 경우를 각각 콘솔에
282
+ 출력해보았습니다. (이해를 돕기 위해 단순화하여 편집한 출력입니다. 크게 신경쓰지 않아도 되는
283
+ 파라미터는 생략하였으며, lambda 함수의 reference들은 단순하게 `lambda` 라고만 표기하였습니다.)
284
+
285
+ ```scala
286
+ // 1. map(nothing => nothing)이 추가되지 않은 경우
287
+ OnSuccess(
288
+ Sync(lambda),
289
+ lambda
290
+ )
291
+
292
+ // 2. map(nothing => nothing)이 추가된 경우
293
+ OnSuccess(
294
+ OnSuccess(
295
+ Sync(lambda),
296
+ lambda
297
+ ),
298
+ lambda
299
+ )
300
+ ```
301
+
302
+ 두 ZIO의 구조적인 차이가 확인되시나요?
303
+ 1번의 경우 `OnSuccess` 가 한 겹만 존재하는데 반해, 2번의 경우는 `OnSuccess` 안에 다시 `OnSuccess` 가 존재하는 구조입니다.
304
+ 이렇게 `OnSuccess` 가 한 겹 더 생기는 이유는 `map` 함수는 단순히 `flatMap` 함수 호출을 wrapping하는 식으로 구현되어 있기 때문입니다.
305
+
306
+ ```scala
307
+ def map[B](f: A => B): ZIO[R, E, B] = flatMap(a => ZIO.succeed(f(a)))
308
+ ```
309
+
310
+ 1번의 경우 `OnSuccess` 케이스를 `FiberRuntime` 에서 처리할 때 재귀호출 하는 `runLoop` 의 파라미터로
311
+ 항상 `Sync` 를 넣어주기 때문에 해당 재귀 호출은 중첩된 재귀 호출을 하지 않고 곧바로 리턴될 것입니다.
312
+ 반면 2번의 경우 `OnSuccess` 가 두 번 겹쳐 있으므로, `runLoop` 재귀 호출의 파라미터로 `OnSuccess` 를
313
+ 넣어주게 되고 이는 중첩된 `runLoop` 재귀 호출을 유발합니다. 중첩된 재귀 호출은 Trampoline을 일으킬
314
+ 것이고, 이 때 stack에 있던 중첩된 effect들이 heap (`reifiedStack`)으로 옮겨가면서 메모리를 차지하게
315
+ 됩니다.
316
+
317
+ ### 문제 해결
318
+
319
+ 다시 맨 첫부분의 `scrapAndPublish` 예시로 돌아가서, 해당 함수를 아래와 같이 for-comprehension을
320
+ 사용하지 않도록 바꿈으로써 문제를 해결할 수 있습니다.
321
+
322
+ ```scala
323
+ private def scrapAndPublish: UIO[Nothing] =
324
+ getEvents
325
+ .flatMap(events => publish(events))
326
+ .flatMap(_ => scrapAndPublish.delay(1.second))
327
+ ```
328
+
329
+ 또한
330
+ [better-monadic-for](https://github.com/oleg-py/better-monadic-for?tab=readme-ov-file#final-map-optimization)이라는
331
+ Scala compile plugin을 적용하면 for-comprehension을 없애지 않고도 문제를 해결할 수 있습니다! 이
332
+ 글에서 설명하는 경우와 같이 마지막 부분에 불필요한 `map` 함수를 해당 플러그인이 제거해줍니다.
333
+
334
+ ## 마무리
335
+
336
+ 이번 글에서는 ZIO를 사용하면서 겪었던 OOM 이슈에 대해 그 원인과 해결 방법을 알아보았습니다. 원인을
337
+ 분석해보면서 ZIO의 FiberRuntime 개념, Trampoline 패턴, for-comprehension의 작동 방식 등 흥미로운
338
+ 여러 주제들에 대해서도 함께 공부할 수 있는 시간이었던 것 같습니다.
339
+
340
+ 우리는 기술을 통해 온라인 결제 시장을 혁신하고자 하는 팀입니다.
341
+ 이와 같은 기술적인 고민을 함께 하며 포트원의 미션에 동참하고 싶으신 분들은 언제든지 포트원의 문을 두드려주시면 감사하겠습니다!
@@ -0,0 +1,314 @@
1
+ ---
2
+ title: 포트원 V2 이야기 - 웹훅 재시도, 어디까지 고도화해 보셨나요?
3
+ description: 웹훅의 중요성과 한계점을 살펴보고 포트원 V2에서 웹훅 시스템을 고도화한 이야기를 소개합니다.
4
+ author: LimJiGyu
5
+ date: 2024-02-28T15:00:02.000Z
6
+ tags:
7
+ - Core V2
8
+ - Backend
9
+ - Scala
10
+ - Webhook
11
+ - Architecture
12
+ ---
13
+
14
+ 포트원의 V2 시스템은 고객들에게 더 나은 가치를 제공하기 위해 다양한 방면으로 기술적 고도화를
15
+ 진행했습니다. 기존에 결제 서비스를 운영하며 발생했던 여러 문제들에 대해 새롭게 고민했고, 그 중에는
16
+ 결제 과정에서 반드시 필요한 웹훅 기능도 있었습니다.
17
+
18
+ 웹훅은 포트원과 고객사 서버의 상태를 동기화하는 매우 중요한 수단인데, 기존 시스템을 운영하며 웹훅과
19
+ 관련한 몇 가지 문제점을 발견했고 이를 해결하기 위해 많은 기술적 고민을 거쳤습니다. 지금부터, V2
20
+ 시스템을 설계하며 웹훅과 관련해 고민했던 여러 기술적 문제들과 그 해결 과정을 공유하고자 합니다.
21
+
22
+ ## 웹훅이란?
23
+
24
+ 웹훅은 하나의 시스템이 다른 시스템에 어떤 이벤트가 발생했음을 알리는 방법입니다.
25
+
26
+ 일반적으로 네트워크 통신은 서비스를 제공받는 클라이언트 앱에서 서비스를 제공하는 서버 앱을 호출하는
27
+ 방향으로 이루어지지만, 서비스를 이용하는 과정에서 서버 앱에 특정 이벤트가 발생했는지 확인해야 하는
28
+ 경우가 생길 수 있습니다. 이 경우, 클라이언트는 서버에게 데이터를 조회하는 요청을 규칙적인 주기로
29
+ 전송하여 확인하고자 하는 특정 이벤트가 발생했는지를 반복적으로 확인해야 하는데 이를
30
+ 폴링(Polling)이라고 합니다.
31
+
32
+ 그러나 이는 특정 이벤트가 어느 시점에 발생할 지 모르는 상황에서 클라이언트가 서버를 확인하는 작업을
33
+ 반복하는 것이기 때문에 이벤트가 발생하기 전까지의 무의미한 네트워크 호출 과정에서 인프라 자원의
34
+ 비효율을 발생시키며, 이벤트 처리에 대한 실시간성 역시 좋지 않은 방식입니다. 따라서 특정 이벤트가
35
+ 발생한 경우, 서버 측에서 클라이언트 측으로 HTTP 요청을 발송하여 정보를 역방향으로 전달하는 방식을
36
+ 사용할 수 있고 이를 웹훅이라고 합니다. 그리고 이러한 특성 때문에 웹훅은 Reverse API, Push API 라고도
37
+ 불립니다.
38
+
39
+ (관련 이미지 첨부)
40
+
41
+ 웹훅을 사용하게 되면 클라이언트는 더 이상 이벤트의 발생 시점을 확인하기 위해 서버를 폴링할 필요가
42
+ 없어지게 됩니다. 그 대신 서버에게 클라이언트 측 URL을 미리 전달하고, 이벤트가 발생하면 해당
43
+ 엔드포인트로 서버가 발송한 이벤트 관련 데이터(Payload)를 통해 적절한 이벤트 처리 로직을 실행시킬 수
44
+ 있습니다. 이는 이벤트가 발생한 시점에 서버가 직접 요청하기 때문에 실시간성 또한 매우 높은 방식이라고
45
+ 할 수 있습니다.
46
+
47
+ ## 포트원의 웹훅 사용 방식
48
+
49
+ 포트원은 이러한 웹훅의 특성을 잘 활용하고 있으며 결제 서비스를 제공하는 과정에서 발생하는 여러 이벤트들을 고객사에 전달하고 있습니다.
50
+
51
+ 한 번의 결제 과정에서도 여러 상황에 대한 웹훅을 발송하고 있지만, 비동기 이벤트에 대한 알림을
52
+ 전달하는 웹훅의 특성을 가장 잘 설명하는 대표적인 사례는 가상계좌 결제에서 찾아볼 수 있습니다.
53
+
54
+ 가상계좌 결제란 사용자에게 일시적으로 할당된 계좌로 결제할 수 있는 방법을 말합니다. 이 방식에서는
55
+ 사용자가 계좌를 생성한 이후 입금 기한 내에 계좌로 돈을 입금하면 상품 또는 서비스에 대한 결제로
56
+ 처리됩니다. 이는 전통적인 신용카드 결제와 달리, 고객사 페이지에서 가상계좌가 생성 처리되는 시점
57
+ 이후에 입금이 비동기적으로 이루어지는 결제 방식입니다.
58
+
59
+ 이러한 결제 방식에서, 고객이 입금을 완료했는지를 실시간으로 확인하기 위해 폴링 방식을 사용하는 것은
60
+ 비효율적입니다. 입금 시점 전까지 반복적으로 대역폭을 불필요하게 소모하고, 서버에 부담을 주며, 결제
61
+ 확인에 대한 지연을 초래하기 때문입니다. 반면, 웹훅을 사용하면 입금이 확인되는 즉시 서버에서 고객사의
62
+ 지정된 웹훅 URL로 알림을 보냄으로써 이러한 비효율을 없앨 수 있습니다.
63
+
64
+ 현재 포트원은 가상계좌 방식 입금 통보 외에도 결제 성공 혹은 실패와 같은 다양한 이벤트에 대해서도
65
+ 웹훅을 전달하며, 이는 고객사가 결제 상태를 실시간으로 추적하고 관리하는 데에 도움을 줍니다
66
+
67
+ 포트원에서 웹훅은 단순한 알림 기능을 넘어서는 중요한 역할을 하기도 합니다. 예를 들어, 고객이 결제
68
+ 과정 중에 문제를 겪어 고객사 페이지로 콜백이 오기 전에 결제 창을 비정상적으로 종료하더라도, 포트원
69
+ 서버에서는 결제 승인 시 웹훅을 발송하기 때문에 고객사 서버에서는 이 결제 건을 적절히 처리할 수
70
+ 있습니다. 이는 고객의 결제 경험을 개선하고, 잠재적인 매출 손실을 방지하는 데 도움이 됩니다.
71
+
72
+ 결국, 웹훅은 포트원이 제공하는 결제 시스템에서 여러 이벤트를 처리하기 위해 꼭 필요한 요소이며, 이를
73
+ 통해 고객사는 더 빠르고 효율적인 결제 처리와 함께 향상된 사용자 경험을 제공할 수 있습니다. 포트원은
74
+ 이러한 웹훅 기술을 통해 결제 서비스의 신뢰성과 효율성을 동시에 높이고 있습니다.
75
+
76
+ ## 웹훅 시스템에서 발생하는 문제점
77
+
78
+ 웹훅은 효율적인 이벤트 알림 메커니즘을 제공하지만, 그 자체로 완벽하지는 않습니다. 가장 큰 문제 중 하나는 웹훅 호출이 실패할 경우 발생합니다.
79
+
80
+ 웹훅 호출의 실패는 고객사의 결제 데이터와 이벤트 처리 로직의 누락으로 이어질 수 있기 때문에 적절한
81
+ 처리가 필요합니다. 이 때 웹훅의 재시도 정책이 그에 대한 좋은 방법이 될 수 있습니다. 단순히 한 번의
82
+ 네트워크 호출 실패로 인해 전체 서비스의 비즈니스 로직을 실패로 처리하거나 대체 로직(fallback)으로
83
+ 전환하는 것은, 네트워크 호출을 재시도하는 것과 비교하여 더 큰 리소스 낭비가 될 수 있기 때문입니다.
84
+
85
+ 또한 실제로 웹훅이 실패하는 대부분의 원인은, 웹훅을 받는 시스템에 큰 장애가 발생했거나 명확한
86
+ 비즈니스 로직상의 실패 응답을 주는 경우보다, 네트워크의 일시적인 장애가 있거나 간헐적인 실패
87
+ 응답으로 인해 발생하는 경우가 더 많기 때문에 재시도 정책은 이러한 일시적 문제를 극복하고 웹훅이
88
+ 중요한 데이터를 성공적으로 전송하는 데에 큰 도움을 줍니다.
89
+
90
+ 그러나 단순히 재시도를 하는 것이 문제를 해결하지는 않습니다. 이를테면 네트워크 문제로 인한 시간
91
+ 초과가 발생한 경우, 문제가 해결될 때까지 지속적으로 웹훅이 실패할 수 있습니다. 그리고 이러한
92
+ 상황에서 재시도를 일정 간격 없이 바로 실행한다면, 이는 문제가 있는 네트워크에 더 큰 부담을 주고
93
+ 오히려 상황을 악화시킬 수 있습니다. 만약 서버 또는 중간 네트워크에 일시적인 과부하가 발생하여 요청이
94
+ 지연되고 있는 상황에서, 모든 클라이언트가 연속적으로 최대 3회 재시도를 한다면 네트워크 트래픽은
95
+ 예상치 못하게 최대 3배까지 증가할 수 있습니다.
96
+
97
+ 위와 같은 문제를 방지하기 위해 재시도의 적절한 간격을 결정하기 위한 정책이 필요합니다. 그리고 재시도
98
+ 간격에 대한 정책을 결정하더라도, 이러한 재시도 정책을 기술적으로 구현하여 원하는 웹훅 발송 시점에
99
+ 적절히 재시도를 실행해야 하고, 재시도 이벤트가 시스템의 변경이나 배포 과정에서도 누락되지 않도록
100
+ 안전하게 관리되는 시스템을 설계해야 합니다.
101
+
102
+ 이제부터, 포트원 V2가 어떻게 이러한 문제들을 해결하고 안전한 웹훅 시스템을 설계했는지 살펴보겠습니다.
103
+
104
+ ## 문제 해결, 재시도 시스템 고도화
105
+
106
+ 포트원 V2는 웹훅 시스템의 이러한 문제점들을 해결하기 위해 웹훅 재시도 시스템을 고도화하는 데에
107
+ 중점을 두었습니다. 그리고 이 과정에서 구현한 몇 가지의 핵심 전략들을 소개하고자 합니다.
108
+
109
+ ### 1. Exponential Backoff
110
+
111
+ Exponential Backoff는 재시도 간의 대기 시간을 지수함수(exponential)의 형태로 점차 증가시키는 방법입니다.
112
+
113
+ 예를 들어, 첫 번째 시도를 위한 대기 시간이 1분이면, 두 번째 시도의 대기 시간은 2분, 세 번째 시도는
114
+ 4분이 됩니다. 이렇게 함으로써, 네트워크에 갑작스럽게 부하를 주는 것을 방지하고, 동시에 많은 요청이
115
+ 집중되는 것을 피할 수 있습니다.
116
+
117
+ 만약 모든 재시도 요청이 동일한 시간 간격으로 이루어진다면, 장애 시간동안 재시도 해야할 요청들이
118
+ 누적됨에 따라 네트워크 트래픽이 기하급수적으로 증가하게 됩니다. 따라서 네트워크 트래픽이 적절히
119
+ 분산되도록 만들기 위해 재시도 간격 또한 기하급수적(exponential)으로 증가시킨다고 생각하면 쉽게
120
+ 이해할 수 있습니다. 이를 통해 장애 시간 동안 재시도 해야 할 요청 수가 증가하더라도 단위 시간 당
121
+ 네트워크 트래픽이 고루 분산되어 웹훅을 수신하는 서버의 부담을 줄일 수 있습니다.
122
+
123
+ 아래는 현재 Scala로 구현된 V2 Core 컴포넌트에서 재시도 간격을 계산하는 코드입니다. 첫 번째 웹훅
124
+ 시도는 바로 진행하기 때문에 0 으로 시작하며 이후부터는 동일한 웹훅에 대한 재시도 횟수가 증가함에
125
+ 따라 1 → 2 → 4 .. → 256 까지 지수함수의 형태로 증가하도록 구현되어 있습니다. 이 때 단위는 분이지만
126
+ 함수를 호출하는 코드에서 사용하기 위해 초 단위로 변경하여 응답하고 있습니다.
127
+
128
+ ```scala
129
+ def calculateDelaySeconds: Duration = {
130
+ """
131
+ 현재 시도 횟수에 따른 지연 시간(초) 계산
132
+ 시도 횟수에 따라 0 -> 1 -> 4 -> 16 -> 64 -> 256 순으로 간격 설정
133
+ """
134
+ if (currentExecutionCount == 1)
135
+ // 첫 번째 시도는 backoff 없이 바로 시도
136
+ return Duration.fromSeconds(0.minute.toSeconds)
137
+ else
138
+ // 두 번째 시도부터 4의 지수 형태로 증가
139
+ return Duration.fromSeconds(
140
+ math.pow(4, currentExecutionCount - 2).toInt.minute.toSeconds
141
+ )
142
+ }
143
+ ```
144
+
145
+ 우리는 Exponential Backoff 전략을 통해 장애 시간 동안 누적되는 재시도 요청들이 단위 시간 당 네트워크
146
+ 트래픽을 기하급수적으로 증가시키는 현상을 막을 수 있었습니다. 그러나, 이 전략을 사용하더라도 여전히
147
+ 동일한 시간대에 발생한 모든 요청은 동일한 시간 간격으로 함께 수행된다는 한계가 존재합니다. 따라서
148
+ 우리는 이러한 한계를 극복하기 위해 Jitter 라는 또 다른 전략을 함께 사용하여 문제를 해결합니다.
149
+
150
+ ### 2. Jitter
151
+
152
+ Jitter는 정해진 시간 간격에 무작위성(randomness)을 추가하여, 모든 재시도가 동시에 이루어지지 않도록
153
+ 합니다. 이는 동일한 시간대에 실패한 각 재시도 이벤트들이 서로 다른 시간에 재시도하도록 만들고 결국,
154
+ 네트워크 트래픽이 골고루 분산되도록 만드는 효과를 가져옵니다. 이 또한 Exponential Backoff 전략과
155
+ 함께 트래픽 급증이나 네트워크 오버로드로 인한 장애를 방지하는 데 도움이 됩니다.
156
+
157
+ 아래 코드는 위에서 보았던 재시도 간격 계산 함수 `calculateDelaySeconds` 에 jittered 함수를 추가로
158
+ 사용한 코드입니다. 내용을 정리하면 Exponential Backoff 전략을 통해 계산된 시간 간격(delay)에 0부터
159
+ 1사이의 무작위 숫자를 곱해서 확률적으로 지연 시간을 결정하는 방식입니다.
160
+
161
+ 이 때 min, max 라는 변수는 무작위 결과가 나올 수 있는 범위를 결정하는 요소라고 볼 수 있습니다.
162
+ Jitter 방식은 다시 Full Jitter, Equal Jitter 로 나눠지는데 이는 무작위 결과가 산출되는 범위를 전체
163
+ 범위로 할 것인지 아니면 최소 지연 시간을 결정하고 남은 범위로 할 것인지의 차이를 가집니다. 예를 들면
164
+ 현재 재시도 간격이 4분으로 정해졌다면, Full Jitter의 경우 0-4분 사이에서 무작위로 선택하지만 Equal
165
+ Jitter의 경우 최소 지연 시간을 2분이라고 설정한 뒤 2-4분 사이에서 무작위로 선택하는 방식입니다.
166
+
167
+ 두 가지 방식 모두 장단점이 있으나 현재 포트원은 성능상 네트워크 트래픽을 더 고루 분산시키는 Full
168
+ Jitter 방식을 사용하고 있으며 이에 따라 min 값을 0으로 사용하고 있습니다.
169
+
170
+ (만약 이 주제에 대해 더 자세하게 알아보고 싶으시면 [AWS Architecture Blog 에서 소개한
171
+ 글](https://aws.amazon.com/ko/blogs/architecture/exponential-backoff-and-jitter/)을 읽어보셔도
172
+ 좋습니다)
173
+
174
+ ```scala
175
+ def calculateDelaySeconds: Duration = {
176
+ """
177
+ 무작위 시간 추가 (jitter)
178
+ """
179
+ if (currentExecutionCount == 1)
180
+ // 첫 번째 시도는 backoff 없이 바로 시도
181
+ return Duration.fromSeconds(
182
+ jittered(
183
+ 0.minute.toSeconds
184
+ ).toLong
185
+ )
186
+ else
187
+ // 두 번째 시도부터 4의 지수 형태로 증가
188
+ return Duration.fromSeconds(
189
+ jittered(
190
+ math.pow(4, currentExecutionCount - 2).toInt.minute.toSeconds
191
+ ).toLong
192
+ )
193
+ }
194
+
195
+ private def jittered(delay: Long) = {
196
+ // min, max 를 설정하여 무작위로 정해질 시간의 범위를 조정
197
+ val min = 0.0
198
+ val max = 1.0
199
+ val random = scala.util.Random.nextDouble()
200
+ delay * min * (1 - random) + delay * max * random
201
+ }
202
+ ```
203
+
204
+ 앞서 살펴본 Exponential Backoff, Jitter 전략은 단순히 재시도 간격을 늘리는 것을 넘어서, 전체적인
205
+ 네트워크 부하를 전략적으로 분산시키고 이를 통해 고객사의 장애 상황에 최대한 영향을 주지 않도록
206
+ 시스템을 설계합니다.
207
+
208
+ 우리는 앞서 세운 전략들로 적절한 재시도 간격을 결정할 수 있게 되었습니다. 그렇다면, 이제 기술적으로
209
+ 이러한 정책을 구현할 수 있는 시스템이 필요합니다. 결정된 시간이 지난 후 어떻게 원하는 시점에 실제
210
+ 재시도 요청이 이루어지도록 할 수 있을까요? 그리고 만약 새로운 버전이 배포되면서 서버가 종료되었을
211
+ 때에도 재시도 해야 할 이벤트들이 누락되지 않도록 하려면 어떻게 해야할까요?
212
+
213
+ ### 3. Message Queue - SQS
214
+
215
+ 포트원 V2에서는 위와 같은 요구사항을 달성하기 위해 메시지 큐(SQS)를 활용하여 시스템을 설계했습니다.
216
+
217
+ 물론 재시도 간격을 기술적으로 구현하기 위한 방법에는 여러 가지가 있겠지만, 가장 처음 생각할 수 있는
218
+ 단순한 방법은 외부 인프라를 이용하지 않고 언어나 라이브러리에서 제공하는 타이머를 이용하는
219
+ 방법입니다. 그러나 만약, 서버가 언어 혹은 라이브러리에서 지원하는 타이머와 비동기 구현의 조합을
220
+ 활용하여 직접 애플리케이션 메모리에 재시도 할 이벤트들을 보유하게 되면, 새로운 버전의 배포 시
221
+ 메모리에 저장된 재시도 이벤트들이 모두 손실되는 문제가 발생할 수 있습니다. 이는 웹훅 데이터의
222
+ 누락으로 이어질 수 있어, 안정적인 시스템 운영에 큰 장애가 됩니다. 이를 해결하기 위해, 재시도
223
+ 이벤트들을 서버가 아닌 외부 인프라에 저장할 필요가 있었습니다.
224
+
225
+ 이 때, 데이터를 저장할 수 있는 외부 인프라에는 다양한 종류가 있지만 우리의 시나리오는 재시도 할
226
+ 이벤트들을 일회성으로 등록하고 처리하는 것이었기 때문에 메시지 큐를 사용하는 것이 가장 적합하다고
227
+ 판단했습니다. 그리고 Kafka, Kinesis, RabbitMQ, SQS 등 여러 종류의 메시지 큐 중에서도 SQS를
228
+ 선택했는데, 그 이유는 다음과 같습니다.
229
+
230
+ #### Delay Queue 기능 자체 지원
231
+
232
+ Exponential Backoff와 Jitter와 같은 기법을 적용하기 위해 메시지 처리를 일정 시간 지연시킬 필요가
233
+ 있습니다. 만약 메시지가 지연되지 않고 반복적으로 애플리케이션에 짧은 주기로 도달한다면 인프라 비용이
234
+ 상당히 비효율적으로 소모될 것이기 때문입니다.
235
+
236
+ SQS는 큐 자체적으로 이러한 지연 처리 기능을 지원하며, 각 메시지의 생산 시점에 원하는 지연 시간을 매
237
+ 번 설정할 수 있는 기능을 제공합니다. 이는 결정된 재시도 간격의 기술적 구현을 별도의 로직이나
238
+ 커스터마이징 없이도 SQS 기능만으로 간단하게 달성할 수 있게 해줍니다.
239
+
240
+ #### FIFO 정책을 강제하지 않는 Standard 모드 지원
241
+
242
+ 모든 재시도 이벤트들은 큐에 전송된 순서에 관계 없이 서로 다른 지연시간을 가지고 독립적으로 처리되어야 합니다.
243
+
244
+ 예를 들어, 먼저 전송된 메시지는 16분 후에 처리되어야 하지만 이후에 전송된 다른 메시지는 1분의
245
+ 지연시간을 가질 수 있습니다. 이 때, FIFO 정책이 엄격하게 지켜진다면 어느 메시지의 지연 시간에
246
+ 맞추더라도 두 메시지의 지연 처리 요구사항을 모두 만족시키는 것은 불가능합니다.
247
+
248
+ 그러나 SQS는 Standard와 FIFO 두 가지 종류를 가지고 있는데, Standard 모드에서는 FIFO(First-In,
249
+ First-Out) 정책을 강제하지 않습니다. 이는 각 메시지가 서로 다른 지연시간을 가질 수 있게 하여,
250
+ 메시지들이 독립적으로 운영될 수 있도록 해야 한다는 요구사항을 쉽게 만족시킵니다.
251
+
252
+ #### Dead Letter Queue(DLQ)를 통한 실패 관리
253
+
254
+ 최대 재시도 횟수를 초과한 메시지는 DLQ(Dead Letter Queue)로 전송됩니다. 이를 통해 실패한 메시지에
255
+ 대한 검사와 관리가 가능해져, 시스템의 안정성과 신뢰성을 향상시킬 수 있습니다.
256
+
257
+ #### AWS Fully Managed Service의 이점
258
+
259
+ SQS는 AWS에서 제공하는 완전 관리형 서비스입니다. 따라서 추가적인 인프라 관리를 필요로 하는 다른 메시지 큐들과 달리 시스템에 쉽게 통합할 수 있다는 장점을 가지고 있습니다.
260
+
261
+ 즉, SQS를 통해 앞서 설계한 우리의 재시도 정책을 기술적으로 간단하게 구현하면서도 애플리케이션의
262
+ 변경이나 시스템의 장애에도 손실을 최소화할 수 있는 웹훅 시스템을 구축할 수 있었습니다.
263
+
264
+ ## 최종 아키텍처
265
+
266
+ 최종적으로, 웹훅 시스템의 아키텍처를 그림으로 표현하면 다음과 같습니다.
267
+
268
+ (관련 이미지 첨부)
269
+
270
+ Transaction Service는 V2 시스템에서 결제의 코어 기능을 담당하는 컴포넌트이며 실질적으로 웹훅을
271
+ 발송하는 주체입니다. 그리고 Transaction Webhook Service 는 SQS를 구독하여 웹훅 재전송을 위한 단순한
272
+ 로직을 반복하는 작은 컴포넌트입니다. 이 컴포넌트가 하는 작업은 SQS에 처리할 메시지가 있는지
273
+ 지속적으로 확인하고, 만약 메시지가 있으면 Transaction Service로 웹훅 재전송을 요청한 뒤 메시지를
274
+ 삭제하는 것이 전부입니다. 즉, 웹훅의 발송을 담당하는 컴포넌트와 메시지 큐를 폴링하여 전달하는
275
+ 컴포넌트가 협업하는 구조입니다.
276
+
277
+ 이 시스템이 동작하는 시나리오를 간단히 설명하면 다음과 같습니다.
278
+
279
+ 1. 고객사 서버로 최초 웹훅을 발송합니다.
280
+
281
+ 2. 고객사로부터 응답을 받습니다. 이 때, 정상 응답을 받은 경우 웹훅 프로세스는 완료됩니다.
282
+ 하지만 실패 응답을 받았을 경우 재시도 프로세스가 시작됩니다.
283
+
284
+ 3. 재시도 할 웹훅에 대한 정보를 담아 SQS에 메시지를 전송합니다. 이 때, SQS의 지연 발송 기능을 이용하여
285
+ 재시도 간격 이후에 메시지가 수신될 수 있도록 합니다.
286
+
287
+ 4. 정해진 지연 시간이 지나면 Transaction Webhook Service가 메시지를 수신하고, 재시도 로직을 시작합니다.
288
+
289
+ 5. Transaction Service로 웹훅을 재발송 하라는 요청을 전송합니다.
290
+ Transaction Service는 간단한 Validation 이후 고객사 서버로 웹훅을 재발송합니다.
291
+ 이 때, 성공하면 프로세스가 완료되며 다시 실패한 경우 3번 로직으로 돌아갑니다.
292
+ 그리고 Transaction Webhook Service로 처리가 완료되었다는 응답을 전송합니다.
293
+
294
+ 6. 웹훅 재발송에 대한 응답을 받습니다.
295
+
296
+ 7. 처리한 메시지를 삭제합니다. 웹훅의 성공 여부와 관계없이 정상적으로 응답이 오기만 했다면
297
+ 해당 재발송 이벤트는 이미 처리된 것이기 때문에 메시지를 삭제해도 문제 없습니다.
298
+ 만약 웹훅이 실패했다면, 다음 재발송 이벤트를 위한 SQS 메시지는 Transaction Service가 새롭게 전송했을 것입니다.
299
+
300
+ 포트원 V2 코어에서는 위와 같은 아키텍처로 앞서 설명한 요구사항들을 모두 만족시키는 웹훅 시스템을 구현할 수 있었습니다.
301
+
302
+ ## 마무리
303
+
304
+ 지금까지 포트원의 결제 기능에서 핵심적인 역할을 하는 웹훅에 대해 알아보았으며, V2 시스템에서
305
+ 안전하고 효과적으로 웹훅을 제공하기 위해 시도한 여러 가지 고도화 방법들을 탐구해보았습니다.
306
+
307
+ V2는 이러한 웹훅 시스템 고도화를 통해 고객의 결제 데이터 손실을 최소화하고, 고객사 서버의 장애
308
+ 상황으로 인한 웹훅 재시도 상황에서도 트래픽 부하를 매우 효율적으로 관리할 수 있게 되었습니다.
309
+
310
+ 이 글을 통해 소개한 웹훅 시스템의 고도화 이외에도, 포트원의 V2 시스템은 다양한 기술적 도전과 혁신을
311
+ 통해 계속해서 발전하고 있으며, 앞으로 더 많은 주제와 기술적 성과들을 여러분과 공유할 예정입니다.
312
+ 포트원의 기술 여정에 여러분을 초대하며, 다음 주제에 대한 기대감을 가져주시길 부탁드립니다. 저희는
313
+ 여러분과 함께 성장하며, 더 나은 서비스를 제공하기 위해 끊임없이 노력할 것입니다. 다음 주제에서
314
+ 뵙겠습니다!