<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>realizers</title>
    <link>https://kdg-is.tistory.com/</link>
    <description>내일은 오늘의 나보다 더 나은 내가 되자.</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 18:07:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>realizers</managingEditor>
    <image>
      <title>realizers</title>
      <url>https://tistory1.daumcdn.net/tistory/4485509/attach/b4dcf752a5344295a1f0c015881ee17b</url>
      <link>https://kdg-is.tistory.com</link>
    </image>
    <item>
      <title>Java Thread Deep Dive</title>
      <link>https://kdg-is.tistory.com/entry/Java-Thread-%EB%82%B4%EA%B0%80-%EC%A0%9C%EC%9D%BC-%EC%9E%98-%EC%95%8C%EC%95%84</link>
      <description>&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 4.00.19.png&quot; data-origin-width=&quot;2042&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYfgWU/btsFoIyYlKI/4ve2jGI4fJTuDxXZWlSij0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYfgWU/btsFoIyYlKI/4ve2jGI4fJTuDxXZWlSij0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYfgWU/btsFoIyYlKI/4ve2jGI4fJTuDxXZWlSij0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYfgWU%2FbtsFoIyYlKI%2F4ve2jGI4fJTuDxXZWlSij0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2042&quot; height=&quot;1158&quot; data-filename=&quot;스크린샷 2024-03-03 오후 4.00.19.png&quot; data-origin-width=&quot;2042&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서론&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;자바를 사용하다 보면 Thread에 대해 많이 들어보고, 대략적인 흐름은 알지만 무심하게 지나치게 되는 상황이 있곤 합니다. 그리고 우리는 ThreadPoolExecutor, ForkJoinPool, VirtualThread, Webflux 등 Thread를 다양하게 사용하는 상황에 대비해 기본적인 Thread에 대해 알 필요가 있습니다. 그럼 지금부터 Thread에 대해 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;참고로 V&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;irtualThread에 의해 만들어지는 Thread는 OS와 매칭되는 흐름이 다르기 때문에 해당 글은 &lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;V&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;irtualThread에 의해 만들어진 Thread가 아님을 알립니다.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread란?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;자바 Thread는 JVM에서 사용자 모드 Thread를 생성할 때 System call을 통해 커널에서 생성된 커널 Thread와 1:1 매칭이 이루어지게 됩니다. 그리고 만들어진 Thread는 커널에서 관리하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;즉, 우리가 애플리케이션에서 Thread를 만들었다면 커널 영역에도 Thread가 하나 만들어지고 이에 매칭이 된다라고 생각해 주시면 될 거 같습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;자바에서는 Platform Thread로 정의되어 있으며, OS 플랫폼에 따라 JVM이 사용자 모드 Thread를 Kernel Thread와 매칭하게 됩니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 1.46.02.png&quot; data-origin-width=&quot;2782&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmjxQl/btsFoJdyD8A/kykA6yk6SK8ZrkoFiqrOF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmjxQl/btsFoJdyD8A/kykA6yk6SK8ZrkoFiqrOF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmjxQl/btsFoJdyD8A/kykA6yk6SK8ZrkoFiqrOF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmjxQl%2FbtsFoJdyD8A%2FkykA6yk6SK8ZrkoFiqrOF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2782&quot; height=&quot;476&quot; data-filename=&quot;스크린샷 2024-03-03 오후 1.46.02.png&quot; data-origin-width=&quot;2782&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 구조&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로세스는 기본적으로 code 영역, data 영역, stack 영역, heap 영역으로 나눌 수 있습니다. 프로세스마다 4가지의 영역을 가지고 있기 때문에 메모리도 많이 차지할 뿐만 아니라 프로세스끼리 공유 자원을 사용할 때 오버헤드가 큽니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 하나의 프로세스에 여러 스레드를 사용함으로써 메모리 자원을 효율적으로 사용할 수 있고, 오버헤드 비용이 상대적으로 적어집니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로세스 내에 code 영역, data 영역, heap 영역은 공유하고, 각 스레드마다 stack 영역을 확보함으로써 한정된 자원을 보다 효율적으로 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;보다 자세한 내용을 알고 싶으면 해당 &lt;a title=&quot;링크&quot; href=&quot;https://github.com/kdg0209/realizers/blob/main/self-learning-cs/10%EC%9E%A5%20%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80%20%EC%8A%A4%EB%A0%88%EB%93%9C.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;에서 확인하실 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 실행&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;start 메서드는 Thread를 실행시키는 메서드이며, System call을 통해서 Kernel에 Kernel Thread 생성을 요청하게 됩니다. 이렇게 만들어진 Thread는 OS Scheduler에 의해 실행 순서가 제어되며, JVM은 실행 순서를 제어할 수 없습니다. 그리고 Thread는 독립적으로 실행되며, Thread가 종료된 이후에는 재사용할 수 없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 1.50.11.png&quot; data-origin-width=&quot;2812&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBNwC4/btsFmjfBv0u/qtMfRo8b5KrIUJcKifZZ71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBNwC4/btsFmjfBv0u/qtMfRo8b5KrIUJcKifZZ71/img.png&quot; data-alt=&quot;Thread 생성의 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBNwC4/btsFmjfBv0u/qtMfRo8b5KrIUJcKifZZ71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBNwC4%2FbtsFmjfBv0u%2FqtMfRo8b5KrIUJcKifZZ71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2812&quot; height=&quot;336&quot; data-filename=&quot;스크린샷 2024-03-03 오후 1.50.11.png&quot; data-origin-width=&quot;2812&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread 생성의 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Thread 생성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Main Thread가 새로운 Thread를 생성합니다. (Thread를 만들었지만 Heap영역에 메모리만 할당)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Main Thread가 start 메서드를 호출하여 Thread를 실행시킵니다.(Kernel Thread와 1:1 매칭)&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;start 메서드를 호출하면 내부적으로 native 메서드인 start0 메서드를 호출하여 Kernel에게 Kernel Thread를 생성해 달라고 System call을 요청 보내게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kernel은 System call 요청을 받아 Kernel Thread를 생성하게 되고, Kernel Thread는 자바 Thread와 1:1 매칭이 이루어지게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kernel Thread는 Os Scheduler로부터 CPU를 할당받기 전까지는 실행대기(Runnable) 상태로 머물게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kernel Thread가 Os &lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Scheduler에 의해 CPU를 할당받아 실행 상태가 되면 JVM에서 매칭된 Thread의 run 메서드를 호출하여 작업을 이어나가게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  주의점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;자바에서 Thread를 생성하고 실행할 때 아래와 같은 코드를 작성하여 run 메서드를 호출한다면 이는 새로운 스레드를 생성하는 게 아니라 직접 호출한 Thread의 Stack에서 단지 run 메서드가 실행될 뿐입니다. 따라서 새로운 Thread를 생성하고 반드시 start 메서드를 호출해야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1709441885719&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Thread thread = new Thread();
// 이는 잘못된 방법입니다.
thread.run(); 

// 옳바른 사용 방법
thread.start();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Thread 종료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread는 run 메서드의 로직이 모두 수행되면 자동적으로 종료하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread는 예외가 발생하여 종료된 경우, 다른 Thread에게 영향을 미치지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 lifecycle&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Os에서는 프로세스의 생명주기가 생성상태, 준비상태, 실행상태, 대기상태, 종료상태로 구분되지만 자바 Thread의 생명주기는 조금 다릅니다. 아래 그림을 통해 하나씩 알아보겠습니다. 그리고 &lt;b&gt;이해를 돕기 위해 Runnable와 Running 상태를 구분하였지만 Thread는 Runnable 상태만을 가집니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.05.30.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zOjCU/btsFp7ZxAPC/jK3km7wAt3vieX2fUUKM0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zOjCU/btsFp7ZxAPC/jK3km7wAt3vieX2fUUKM0k/img.png&quot; data-alt=&quot;Thread의 생명 주기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zOjCU/btsFp7ZxAPC/jK3km7wAt3vieX2fUUKM0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzOjCU%2FbtsFp7ZxAPC%2FjK3km7wAt3vieX2fUUKM0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2888&quot; height=&quot;956&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.05.30.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Thread의 생명 주기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;NEW&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread 객체는 생성되었지만 아직 Kernel Thread와 매칭되지 않은 상태이며, Heap 영역에 객체로만 생성되어 있는 상태입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;RUNNABLE&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kernel에게 System call을 요청하여 Kernel Thread가 생성되고, java Thread와 1:1 매칭이 된 상태입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;만들어진 Thread는 바로 실행되는 게 아니라 언제든지 실행할 준비가 되어 있는 상태입니다. 이때 CPU의 자원을 할당받으면 실행되게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;CPU의 자원을 받아 실행 상태가 된다면 내부적으로 Thread의 run 메서드를 호출하게 됩니다. 또한 context-switch가 발생하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;운영체제에서 준비 상태로 보면 이해하기 편할 거 같습니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;WAITING&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Thread가 실행 상태에 있다가 다른 Thread의 특정 작업이 끝나길 기다리는 상태입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Waiting 상태에 있는 Thread는 다른 Thread에 의해 notify를 받을 때까지 혹은 join 메서드로 인해 작업을 완료하거나 인터럽트가 발생할 때까지 대기하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;TIMED_WAITING&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;지정된 시간 동안 Thread는 일시 정지 상태가 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 일시 정지 시간이 길어지고, CPU의 자원을 할당받지 못하는 상황이 발생하면 기아 상태가 발생하게 되는데 시간을 지정함으로써 이를 피할 수 있습니다. 그리고 Os 내부적으로 에이징 기법을 사용하여 기아 상태를 방지합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 상태에서 Runnable 상태로 갈 수 있는데 이때는 지정된 시간이 만료되거나, 인터럽트가 발생한 거나, notify 같은 메서드로 인해 통지를 받을 때 상태를 바꿀 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;BLOCKED&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;critical section(임계 영역)에서 작업 중인 Thread가 있는 상황에서 해당 영역에 접근을 시도하는 Thread가 있으면 해당 Thread는 blocking 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;critical section에서 작업 중인 Thread가 context-switch가 발생해도 다른 Thread는 critical section에 접근할 수 없습니다. 무조건 lock을 획득하고 나서 접근을 시도할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;TERMINATED&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread가 실행이 완료되거나 예외로 인해 예기치 않게 종료된 상태입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;종료된 Thread는 재사용할 수 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 주요 API&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Thread를 사용하다 보면 제일 많이 쓰는? sleep 메서드와 join 메서드에 대해 간략하게 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Sleep 메서드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;sleep 메서드는 지정된 시간 동안 Thread를 일시 정지 상태로 만들고, 지정된 시간이 만료되면 Runable 상태가 됩니다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;sleep 메서드는 native 메서드를 통해서 System call을 통해 Kernel 모드로 수행 후 사용자 모드로 전환됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;critical section에서 sleep된 Thread는 획득한 Lock을 잃지 않고 계속 유지하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;sleep된 Thread에게 인터럽트가 발생한 경우 해당 Thread는 깨어나게 되고, 실행 대기 상태에서 실행 상태로 전환되어 InterruptedException을 처리하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;지정된 시간이 만료한 경우&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.25.02.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TSZme/btsFuIYVr4v/IKQKvJ9IWLP6Nlkbw5koKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TSZme/btsFuIYVr4v/IKQKvJ9IWLP6Nlkbw5koKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TSZme/btsFuIYVr4v/IKQKvJ9IWLP6Nlkbw5koKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTSZme%2FbtsFuIYVr4v%2FIKQKvJ9IWLP6Nlkbw5koKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2888&quot; height=&quot;300&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.25.02.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;sleep중 인터럽트가 발생한 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.25.18.png&quot; data-origin-width=&quot;2894&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TDi2n/btsFm7Tzmwu/7uN5fwu5CduI6SNZ7YAln0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TDi2n/btsFm7Tzmwu/7uN5fwu5CduI6SNZ7YAln0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TDi2n/btsFm7Tzmwu/7uN5fwu5CduI6SNZ7YAln0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTDi2n%2FbtsFm7Tzmwu%2F7uN5fwu5CduI6SNZ7YAln0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2894&quot; height=&quot;494&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.25.18.png&quot; data-origin-width=&quot;2894&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Sleep(0)과 Sleep(N)의 차이&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;sleep 메서드는 native 메서드이기 때문에 sleep 메서드를 호출하면 System call을 통해 User 모드에서 Kernel 모드로 전환됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다른 Thread에게 명확하게 실행을 양보하기 위해서는 sleep(0)이 아닌 sleep(n)을 사용해야 합니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt; Sleep(0)의 동작 방식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread가 Kernel 모드로 전환 후 Os Scheduler는 현재 Thread와 동일한 우선순위를 가지는 다른 Thread가 있는지 확인하고, 다른 Thread가 있는 상황에서 그 Thread가 Runnable 상태라면 그 Thread에게 CPU를 할당함으로써 context-switch가 발생하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;만약 우선순위가 동일한 Runnable 상태의 다른 Thread가 없다면 Os Scheduler는 현재 Thread에게 CPU를 할당함으로써 context-swith가 발생하지 않게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.31.01.png&quot; data-origin-width=&quot;2882&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9ErLS/btsFlWLHgVt/8dxXBh1TRIExwawqHpEEKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9ErLS/btsFlWLHgVt/8dxXBh1TRIExwawqHpEEKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9ErLS/btsFlWLHgVt/8dxXBh1TRIExwawqHpEEKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9ErLS%2FbtsFlWLHgVt%2F8dxXBh1TRIExwawqHpEEKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2882&quot; height=&quot;670&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.31.01.png&quot; data-origin-width=&quot;2882&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt; Sleep(N)의 동작 방식&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread가 Kernel 모드로 전환 후 Os Scheduer는 현재 Thread의 상태와 상관없이 일시 정지 상태로 두고, 다른 Thread에게 CPU를 할당함으로써 context-swith가 발생하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.32.40.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMsCOt/btsFogpaHOj/XOD6orSHwFHIHvK2NbdDhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMsCOt/btsFogpaHOj/XOD6orSHwFHIHvK2NbdDhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMsCOt/btsFogpaHOj/XOD6orSHwFHIHvK2NbdDhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMsCOt%2FbtsFogpaHOj%2FXOD6orSHwFHIHvK2NbdDhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2874&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.32.40.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Join 메서드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;join 메서드는 한 Thread가 다른 Thread의 작업이 종료될 때까지 실행을 중지하고, 대기 상태에 머무르다가 해당 Thread가 종료하면 실행 대기 상태로 전환되었다가 실행 상태가 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 순서를 제어하거나 다른 Thread의 작업을 기다리거나 순차적인 흐름을 구성할 때 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Object 클래스의 native wait() 메서드로 연결되며 System call을 통해 Kernel 모드로 수행됩니다. 또한 내부적으로 wait와 notify 메서드를 가지고 제어하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;정상적인 wait와 notify의 흐름&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.37.49.png&quot; data-origin-width=&quot;2886&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ALOT0/btsFp8RG7EA/KteIAeo12kCB9JC5iZhd5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ALOT0/btsFp8RG7EA/KteIAeo12kCB9JC5iZhd5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ALOT0/btsFp8RG7EA/KteIAeo12kCB9JC5iZhd5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FALOT0%2FbtsFp8RG7EA%2FKteIAeo12kCB9JC5iZhd5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2886&quot; height=&quot;440&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.37.49.png&quot; data-origin-width=&quot;2886&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709444316497&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) throws InterruptedException {

        Thread threadB = new Thread(() -&amp;gt; {
            try {
                System.out.println(&quot;threadB가 실행되고 있습니다.&quot;);
                Thread.sleep(5000);
                System.out.println(&quot;threadB의 작업이 완료되었습니다.&quot;);
            } catch (InterruptedException e) {}
        });

        Thread threadA = new Thread(() -&amp;gt; {
            try {
                System.out.println(&quot;threadA가 실행되고 있습니다.&quot;);
                threadB.join();
                System.out.println(&quot;threadA는 threadB의 작업이 완료되고 후속 로직을 수행하고 있습니다.&quot;);
            } catch (InterruptedException e) {}
        });

        threadA.start();
        threadB.start();
}
// 결과
threadA가 실행되고 있습니다.
threadB가 실행되고 있습니다.
threadB의 작업이 완료되었습니다.
threadA는 threadB의 작업이 완료되고 후속 로직을 수행하고 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;인터럽트가 발생한 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.40.06.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7YqcF/btsFobuAuNX/BTsMu2w0qr3vBuycSjy4e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7YqcF/btsFobuAuNX/BTsMu2w0qr3vBuycSjy4e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7YqcF/btsFobuAuNX/BTsMu2w0qr3vBuycSjy4e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7YqcF%2FbtsFobuAuNX%2FBTsMu2w0qr3vBuycSjy4e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2888&quot; height=&quot;670&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.40.06.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709444396037&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadExample {

    public static void main(String[] args) throws InterruptedException {

        Thread threadB = new Thread(() -&amp;gt; {
            try {
                System.out.println(&quot;threadB가 실행되고 있습니다.&quot;);
                Thread.sleep(5000);
                System.out.println(&quot;threadB의 작업이 완료되었습니다.&quot;);
            } catch (InterruptedException e) {
                System.out.println(&quot;threadB도 인터럽트에 의해 실행이 중지됩니다.&quot;);
            }
        });

        Thread threadA = new Thread(() -&amp;gt; {
            try {
                System.out.println(&quot;threadA가 실행되고 있습니다.&quot;);
                threadB.join();
                System.out.println(&quot;threadA는 threadB의 작업이 완료되고 후속 로직을 수행하고 있습니다.&quot;);
            } catch (InterruptedException e) {
                System.out.println(&quot;threadA는 인터럽트가 발생했습니다.&quot;);
            }
        });

        Thread threadC = new Thread(() -&amp;gt; {
            System.out.println(&quot;threadC는 threadA에게 인터럽트를 발생시킵니다.&quot;);
            threadA.interrupt();
        });

        threadA.start();
        threadB.start();
        threadC.start();
    }
}
// 결과
threadB가 실행되고 있습니다.
threadC는 threadA에게 인터럽트를 발생시킵니다.
threadA가 실행되고 있습니다.
threadA는 인터럽트가 발생했습니다.
threadB의 작업이 완료되었습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 예외처리&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;우리는 흔히 Spring Boot에서 &lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;ThreadPoolTaskExecutor를 빈으로 정의하고,&amp;nbsp;&lt;/span&gt;@Async 어노테이션을 사용한 경험이 있거나 Completablefuture를 사용한 경험이 있을 것입니다. 이때 예외를 처리해야 하는데 우선 기본적으로 어떻게 할 수 있는지 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;기본적으로 Thread의 run 메서드는 예외를 던질 수 없기 때문에 예외가 발생한 경우 run 메서드 안에서만 예외를 처리해야 합니다. 그렇기 때문에 자바에서 Thread가 비정상적으로 종료되거나, 특정한 예외를 Thread 외부에서 캐치하기 위해서 자바에서는 UncaughtExceptionHandler 인터페이스를 제공하고 있습니다. (다만 상황에 따라 Thread 생성 시 Runnable 인터페이스가 아닌 Callable 인터페이스를 사용한다면 예외처리 가능합니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;  &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;UncaughtExceptionHandler&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;RuntimeException 예외로 인해 Thread가 비정상적으로 종료되었을 때 호출되는 인터페이스 핸들러입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;어떤 원인으로 Thread가 종료되었는지 파악할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thead 각각으로 예외 핸들러를 설정할 수 있을 뿐 아니라, 기본적인 핸들러를 통해서도 설정할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1709445096831&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadExample {

    // 커스텀하게 핸들러 작성
    private static final Thread.UncaughtExceptionHandler HANDLER = (thread, exception) -&amp;gt; {
        System.out.println(&quot;threadName: &quot; + thread.getName() + &quot;, message: &quot; + exception.getMessage());
    };

    public static void main(String[] args) throws InterruptedException {

        // 기본적인 예외 핸들러 작성
        Thread.setDefaultUncaughtExceptionHandler(((t, e) -&amp;gt; {
            System.out.println(t.getName() + &quot;에서 에외 발생: &quot; + e.getMessage());
        }));

        Thread threadA = new Thread(() -&amp;gt; {
            System.out.println(&quot;threadA가 실행되고 있습니다.&quot;);
            throw new RuntimeException(&quot;실행 도중 예기치 않은 이유로 예외가 발생하였습니다.&quot;);
        });
        // 핸들러 설정
        threadA.setUncaughtExceptionHandler(HANDLER);


        Thread threadB = new Thread(() -&amp;gt; {
            System.out.println(&quot;threadB가 실행되고 있습니다.&quot;);
            throw new NullPointerException(&quot;NPE 발생&quot;);
        });

        threadA.start();
        threadB.start();
    }
}
// 결과
threadA가 실행되고 있습니다.
threadA가 실행되고 있습니다.
Thread-1에서 에외 발생: NPE 발생
threadName: Thread-0, message: 실행 도중 예기치 않은 이유로 예외가 발생하였습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread의 물품 보관소인 ThreadLocal&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션을 만들다 보면 우리는 Thread 각각에게 데이터를 저장하고 싶을 때가 있을 수 있습니다. 예를 들어 spring security를 사용할 때 각 사용자의 정보는 Thread마다 따로 보관해야 할 때가 있죠. 이때 ThreadLocal을 사용하여 문제를 해결할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;  ThreadLocal이란?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;자바에서 Thread는 오직 자신만 접근하여 읽고 쓸 수 있는 로컬 저장소를 제공하는데 이를 ThreadLocal이라 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;ThreadLocal은 Thread간 격리되어 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Thread는 ThreadLocal에 저장된 값을 특정 위치나 시점에 상관없이 어디서나 전역변수처럼 접근하여 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.59.10.png&quot; data-origin-width=&quot;2882&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciJGAf/btsFpQRaL4N/cCPDUAdPQZ8z7F0wHNHYlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciJGAf/btsFpQRaL4N/cCPDUAdPQZ8z7F0wHNHYlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciJGAf/btsFpQRaL4N/cCPDUAdPQZ8z7F0wHNHYlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciJGAf%2FbtsFpQRaL4N%2FcCPDUAdPQZ8z7F0wHNHYlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2882&quot; height=&quot;494&quot; data-filename=&quot;스크린샷 2024-03-03 오후 2.59.10.png&quot; data-origin-width=&quot;2882&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  ThreadLocal의 동작 원리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터 저장시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.00.17.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sNElC/btsFoJYWDmG/764siKQGpDRfdRJRFwbkk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sNElC/btsFoJYWDmG/764siKQGpDRfdRJRFwbkk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sNElC/btsFoJYWDmG/764siKQGpDRfdRJRFwbkk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsNElC%2FbtsFoJYWDmG%2F764siKQGpDRfdRJRFwbkk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2888&quot; height=&quot;584&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.00.17.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709445702193&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadExample {

    private static final ThreadLocal&amp;lt;String&amp;gt; THREAD_LOCAL = new ThreadLocal&amp;lt;&amp;gt;();

    public static void main(String[] args) throws InterruptedException {

        Thread threadA = new Thread(() -&amp;gt; {
            System.out.println(Thread.currentThread().getName() + &quot;의 값: &quot; + THREAD_LOCAL.get());
            THREAD_LOCAL.set(&quot;threadA에 값 추가 hello world?!&quot;);
            System.out.println(Thread.currentThread().getName() + &quot;의 값: &quot; + THREAD_LOCAL.get());

            // 사용 후 반드시 remove 호출
            THREAD_LOCAL.remove();
        });

        Thread threadB = new Thread(() -&amp;gt; {
            System.out.println(Thread.currentThread().getName() + &quot;의 값: &quot; + THREAD_LOCAL.get());
            THREAD_LOCAL.set(&quot;threadB에 값 추가 HELLO WORLD?!&quot;);
            System.out.println(Thread.currentThread().getName() + &quot;의 값: &quot; + THREAD_LOCAL.get());

            // 사용 후 반드시 remove 호출
            THREAD_LOCAL.remove();
        });

        threadA.setName(&quot;threadA&quot;);
        threadA.start();

        threadB.setName(&quot;threadB&quot;);
        threadB.start();
    }
}
// 결과
threadA의 값: null
threadB의 값: null
threadA의 값: threadA에 값 추가 hello world?!
threadB의 값: threadB에 값 추가 HELLO WORLD?!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터 조회 시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.01.17.png&quot; data-origin-width=&quot;2886&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AoU3Y/btsFm8LD8lG/FrTY562PvCwh3UktrrMfu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AoU3Y/btsFm8LD8lG/FrTY562PvCwh3UktrrMfu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AoU3Y/btsFm8LD8lG/FrTY562PvCwh3UktrrMfu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAoU3Y%2FbtsFm8LD8lG%2FFrTY562PvCwh3UktrrMfu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2886&quot; height=&quot;728&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.01.17.png&quot; data-origin-width=&quot;2886&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  ThreadLocal 사용 시 주의점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;ThreadLocal에 저장된 값은 Thread마다 독립적으로 저장되기 때문에 데이터를 삭제하지 않아도 메모리를 점유하는 것 외에 문제가 발생하지 않습니다.(이 또한 문제이기 하죠 삭제하지 않으면 메모리 누수 ^_^)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그리고 스프링은 ThreadPool 기반이므로 ThreadLocal 사용 시 반드시 remove 메서드를 호출해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;remove 메서드를 호출하지 않는다면 ThreadPool은 Thread를 재사용하기 때문에 현재 Thread는 이전 Thread에서 삭제하지 않은 데이터를 참조할 수 있기 때문에 문제가 발생하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  ThreadLocal에 데이터 저장 시 흐름&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;ThreadLocal에 데이터를 저장하는 순서는 대락적으로 아래 그림처럼 흘러갑니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트 A의 요청이 왔을 때 WAS는 ThreadPool에서 가용 Thread 하나를 조회하게 됩니다. 그리고 Thread-A가 할당되고, 클라이언트A의 정보는 Thread-A의 ThreadLocal에 저장됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.07.43.png&quot; data-origin-width=&quot;2890&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GN59m/btsFs0etECa/okuoqfHqYfEq1Hyps7PntK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GN59m/btsFs0etECa/okuoqfHqYfEq1Hyps7PntK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GN59m/btsFs0etECa/okuoqfHqYfEq1Hyps7PntK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGN59m%2FbtsFs0etECa%2FokuoqfHqYfEq1Hyps7PntK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2890&quot; height=&quot;808&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.07.43.png&quot; data-origin-width=&quot;2890&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  ThreadLocal의 remove 메서드를 호출하지 않는다면?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트 B의 요청이 들어온 경우 이번에도 똑같이 WAS는 ThreadPool에서 가용 Thread 하나를 조회하게 됩니다. 여기서 Thread-A가 할당될 수도 있고, 다른 Thread가 할당될 수도 있지만 편의상 Thread-A가 할당된다고 가정하겠습니다. 이때 Thread-A는 기존 클라이언트 A의 정보를 저장하고 있었지만 remove 메서드를 호출하지 않아 더미 데이터를 보관하고 있었습니다. 이때 get 메서드를 통해 조회하면 예기치 않게 클라이언트A의 정보를 반환하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.10.26.png&quot; data-origin-width=&quot;2868&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2L9NT/btsFuLOS4NW/fZ5e9KcUtVYTix0hBJ1CGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2L9NT/btsFuLOS4NW/fZ5e9KcUtVYTix0hBJ1CGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2L9NT/btsFuLOS4NW/fZ5e9KcUtVYTix0hBJ1CGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2L9NT%2FbtsFuLOS4NW%2FfZ5e9KcUtVYTix0hBJ1CGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2868&quot; height=&quot;804&quot; data-filename=&quot;스크린샷 2024-03-03 오후 3.10.26.png&quot; data-origin-width=&quot;2868&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;User Thread와 Daemon Thread&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;ThreadPoolExecutor를 사용하다 보면 개발자가 Thread를 Daemon Thread로 만들지 User Thread로 만들지 한 번쯤 고민하게 됩니다. 이때 각각이 무엇인지 살펴보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;우선 자바에서 User Thread에 의해 만들어진 Thread는 User Thread이고, Daemon Thread에 의해 만들어진 Thread는 Daemon Thread입니다. 즉 자식 Thread는 부모 Thread를 상속받게 됩니다.&amp;nbsp;&lt;br /&gt;그리고 자바 애플리케이션이 시작되면 JVM은 User Thread인 Main Thread와 Daemon Thread를 동시에 생성하고 시작하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;  Main Thread&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Main Thread는 자바 애플리케이션에서 가장 중요한 Thread입니다. JVM은 애플리케이션이 시작되면 Main Thread를 생성하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Main Thread에서 여러 하위 스레드를 추가할 수도 있고, 만들어진 하위 스레드에 또 하위 스레드를 만들 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Main Thread는 User Thread이므로 Main Thread에 의해 만들어진 스레드 또한 User Thread입니다. 하지만 setDaemon 메서드를 통해 Daemon Thread로 만들 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;  User Thread&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;User Thread는 Main Thread에서 직접 생성한 스레드입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;User Thread는 각각 독립적인 생명주기를 가지고 실행되며, Main Thread를 포함한 모든 사용자 스레드가 종료되면 애플리케이션이 종료하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;User Thread는 foreground에서 실행되는 높은 우선순위를 가지며, JVM은 사용자 스레드가 스스로 종료될 때까지 애플리케이션을 종료하지 않고 기다립니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자바는 &lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;ThreadPoolExecutor를 사용하여 User Thread를 만들게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;   Daemon Thread&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Daemon Thread는 JVM에서 생성한 스레드이거나 직접 Daemon Thread로 생성한 경우입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 User Thread가 완료되면 Daemon Thread의 실행 여부와 상관없이 JVM은 Daemon Thread를 강제 종료하고 애플리케이션이 종료되게 됩니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Daemon Thread의 생명주기는 User Thread에 따라 다르며, 낮은 우선순위를 가지고 background로 실행됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Daemon Thread는 User Thread를 보조 및 지원하는 성격을 가진 스레드로 보통 사용자 스레드를 방해하지 않으며 백그라운드에서 자동적으로 작동되는 기능을 가진 스레드입니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;자바가 제공하는 ForkJoinPool은 &lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;Daemon Thread를 생성하게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1709447122080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class ThreadExample {

    public static void main(String[] args) throws InterruptedException {

        Thread userThread = new Thread(() -&amp;gt; {
            System.out.println(&quot;1. 스레드의 데몬 여부: &quot;+ Thread.currentThread().isDaemon());
            new Thread(() -&amp;gt; System.out.println(&quot;1-1. 사용자 스레드에 의해 만들어진 스레드의 데몬 여부: &quot;+ Thread.currentThread().isDaemon()))
                    .start();
        });
        Thread daemonThread = new Thread(() -&amp;gt; {
            System.out.println(&quot;2. 스레드의 데몬 여부: &quot;+ Thread.currentThread().isDaemon());
            new Thread(() -&amp;gt; System.out.println(&quot;2-2. 데몬 스레드에 의해 만들어진 스레드의 데몬 여부: &quot;+ Thread.currentThread().isDaemon()))
                    .start();
        });
        daemonThread.setDaemon(true);

        userThread.start();
        daemonThread.start();
    }
}
// 결과
1. 스레드의 데몬 여부: false
2. 스레드의 데몬 여부: true
2-2. 데몬 스레드에 의해 만들어진 스레드의 데몬 여부: true
1-1. 사용자 스레드에 의해 만들어진 스레드의 데몬 여부: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꺼억...!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA/JAVA기본</category>
      <category>Java Thread</category>
      <category>java ThreadLocal</category>
      <category>java userThread와 DaemonThread</category>
      <category>자바 스레드</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/496</guid>
      <comments>https://kdg-is.tistory.com/entry/Java-Thread-%EB%82%B4%EA%B0%80-%EC%A0%9C%EC%9D%BC-%EC%9E%98-%EC%95%8C%EC%95%84#entry496comment</comments>
      <pubDate>Sun, 3 Mar 2024 16:02:43 +0900</pubDate>
    </item>
    <item>
      <title>Transactional Outbox Pattern with Polling Publisher</title>
      <link>https://kdg-is.tistory.com/entry/Transactional-Outbox-Pattern-with-Polling-Publisher</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서론&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Transactional Outbox Pattern에 대해서 궁금하시다면 해당 링크를 참고해 주세요. &lt;a title=&quot;Transactional Outbox Pattern&quot; href=&quot;https://kdg-is.tistory.com/entry/MSA-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-OutBox-Pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Transactional Outbox Pattern&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이번 글에서는 Spring Boot와 Polling Publisher를 활용하여 어떻게 Transactional Outbox Pattern을 구현할 수 있는지, 그리고 주의점이 무엇인지 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;동작 과정&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-06 오후 9.52.54.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WCvNR/btsBuiLxz87/ji703GQNMxt8219UsuKBtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WCvNR/btsBuiLxz87/ji703GQNMxt8219UsuKBtK/img.png&quot; data-alt=&quot;동작 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WCvNR/btsBuiLxz87/ji703GQNMxt8219UsuKBtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWCvNR%2FbtsBuiLxz87%2Fji703GQNMxt8219UsuKBtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1018&quot; height=&quot;457&quot; data-filename=&quot;스크린샷 2023-12-06 오후 9.52.54.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동작 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위 동작 과정은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트의 요청이 발생합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Member Service에서 Member 테이블에 대해 Insert, Update, Delete가 발생하면 Outbox 테이블에도 변경사항을 기록할 수 있도록 데이터를 저장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Message Relay는 주기적으로 Outbox 테이블을 읽고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Message Reply에서 Outbox 테이블에 새로운 데이터가 있으면 Message Broker로 이벤트를 발행합니다. 그리고 해당 이벤트를 Outbox 테이블에서 삭제합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt; 필자가 구상한 아키텍처&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-28 오후 2.56.10.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2A7Ku/btsCJj91JTG/EgYa4uiCxV3Sp8Xm8osc31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2A7Ku/btsCJj91JTG/EgYa4uiCxV3Sp8Xm8osc31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2A7Ku/btsCJj91JTG/EgYa4uiCxV3Sp8Xm8osc31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2A7Ku%2FbtsCJj91JTG%2FEgYa4uiCxV3Sp8Xm8osc31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2056&quot; height=&quot;770&quot; data-filename=&quot;스크린샷 2023-12-28 오후 2.56.10.png&quot; data-origin-width=&quot;2056&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;위 과정을 진행하면서 어떠한 것을 고민했는지 무엇을 주의해야 하는지 먼저 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; text-align: start;&quot;&gt;  고민점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;처음에 간단히 구현하고자 outbox-service 인스턴스가 필요할까? 라는 고민을 했었다. 하지만 실제 환경에서 coupon-service 인스턴스가 주기적으로 Auth-DB를 polling 하고 있다면 서비스들 간에 강결합이 발생하고, 추후 유지보수나 coupon-service가 혹여나 스케일 아웃이 발생하면 polling 하고 있던 스케줄러에도 문제가 발생하므로 나누는 게 좋을 거 같다는 생각이 들어서 구분을 지었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;outbox-service에서 주기적으로 Kafka로 메시지를 발송하는데 이때 Kafka에 문제가 발생하여 중복 메시지가 발행될 때 coupon-service에서는 어떻게 멱등성으로 처리할 수 있을까에 대한 고민을 했었다. (이 부분은 추후 코드로 설명)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  주의점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;트랜잭셔널 아웃박스 패턴 사용시 이벤트가 중복해서 발행될 수 있습니다. 따라서 소비하는 인스턴스에서는 이를 멱등하게 처리할 수 있어야 합니다.(필자는 Tsid를 사용하여 처리)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;트랜잭션을 지원하지 않는 NoSQL DB를 사용하는 경우 아웃박스에 대한 정보를 객체에 담아 처리할 수 있다고 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;메시지 순서가 중요한 경우 아웃박스 테이블에 시퀀셜한 아이디를 부여하거나 이벤트를 발행할 때 생성일을 저장하여 Message Relay에서 특정 값 순서로 쿼리를 작성하면 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;코드 살펴보자&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;1. 회원가입 요청으로 인한 auth table 및 outbox table 저장&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 코드를 살여보면 MemberCreateService에서 auth table에 저장을 하고, 이벤트를 발행시키는 것을 알 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트는 @EventListener 어노테이션을 사용하여 동기적으로 처리하고 있습니다. 그렇기 때문에 auth table에 저장 시 문제가 발생하면 outbox table에도 저장되지 않고, 반대인 경우에도 저장되지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703745057413&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@Transactional
@RequiredArgsConstructor
public class MemberCreateService implements MemberCreateUsecase {

    private final CreateMemberPort createMemberPort;
    private final ValidationMemberPort validationPort;
    private final ApplicationEventPublisher publisher;

    @Override
    public MemberCreateResponse create(MemberCreateRequest request) {
        validationPort.validateMemberId(request.id());
        validationPort.validateMemberEmail(request.email());

        var memberEmail = new MemberEmail(request.email());
        var member = Member.create(request.id(), request.password(), request.name(), request.nickName(), memberEmail);
        
        // auth table 저장
        var entity = createMemberPort.createMember(member);

        // 멤버 생성 이벤트 발행
        publisher.publishEvent(new MemberCreatedEvent(member.getId(), member.getName(), member.getEmail()));

        return new MemberCreateResponse(entity.converterPKToString());
    }
}

@Component
@RequiredArgsConstructor
public class MemberEventListener {

    private final MemberOutBoxCreateService service;

    @EventListener(MemberCreatedEvent.class)
    public void handle(MemberCreatedEvent event) {
        service.create(event);
    }
}

@Service
@Transactional
@RequiredArgsConstructor
public class MemberOutBoxCreateService {

    private final CreateMemberOutBoxPort createMemberOutBoxPort;

    @SneakyThrows
    public void create(MemberCreatedEvent event) {
        var objectMapper = new ObjectMapper();
        var payload = objectMapper.writeValueAsString(event);
        var memberOutBox = MemberOutBox.create(event.id(), payload);

        // outbox table 저장
        createMemberOutBoxPort.createMemberOutBox(memberOutBox);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;2. Outbox-Service에 의한 Polling Publisher&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;스케줄러는 10초마다&amp;nbsp;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: left;&quot;&gt;outbox table에서 status가 READY인 데이터를 조회하고, 상태를 DONE으로 변경시키면서 Kafka에게 메시지를 발행시킵니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703745464865&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@Transactional
@RequiredArgsConstructor
public class OutboxFindService implements OutboxFindPort {

    private final MemberOutBoxDao memberOutBoxDao;

    @Override
    public List&amp;lt;OutboxResponse&amp;gt; findAll() {
        // outbox table에서 status가 READY인 데이터 조회
        var outBoxes = memberOutBoxDao.findAll();

        // 상태 변경(READY -&amp;gt; DONE)
        outBoxes.forEach(MemberOutBox::updateToDoneStatus);

        return outBoxes.stream()
                .map(outBox -&amp;gt; new OutboxResponse(outBox.getId(), outBox.getMemberId(), outBox.getPayload()))
                .collect(Collectors.toList());
    }
}

public interface OutboxFindPort {

    List&amp;lt;OutboxResponse&amp;gt; findAll();
}

@Slf4j
@Component
@RequiredArgsConstructor
public class OutBoxScheduler {

    private final OutboxFindPort findPort;
    private final KafkaProducer kafkaProducer;

    @Scheduled(cron = &quot;0/10 * * * * *&quot;)
    public void scheduler() {
        findPort.findAll()
                .forEach(kafkaProducer::sendMessage);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;3. Coupon-Service의 데이터 저장&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;coupon-service는 Kafka의 특정 토픽을 구독하고 있습니다. 구독을 하고 있다가 메시지를 받으면 Service를 호출하게 됩니다. 그리고 서비스에서는 쿠폰과 쿠폰 히스토리를 저장하고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;coupon-service에서는 Kafka로부터 받은 메시지를 &lt;b&gt;멱등성&lt;/b&gt;이 보장되도록 비지니스 로직을 구성해야 합니다. 필자는 아직 쿠폰 히스토리 테이블에 outboxId를 유니크 설정을 하여 저장을 시킴으로써 멱등성을 보장하고 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703745781838&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Component
@RequiredArgsConstructor
public class KafkaConsumer {

    private final CouponCreateUsecase createPort;

    @KafkaListener(topics = &quot;outbox&quot;)
    public void consume(MemberOutboxMessage message) {
        log.info(&quot;Consumer message : {}&quot;, message);

        var request = new CouponCreateRequest(message.outBoxId(), message.id());
        createPort.create(request);
    }
}

public interface CouponCreateUsecase {

    void create(CouponCreateRequest request);
}

@Service
@Transactional
@RequiredArgsConstructor
public class CouponCreateService implements CouponCreateUsecase {

    private static final String COUPON_NAME = &quot;가입 축하 쿠폰&quot;;

    private final CouponCreatePort createPort;
    private final CouponHistoryCreatePort historyCreatePort;

    @Override
    public void create(CouponCreateRequest request) {

        // 쿠폰 저장
        var coupon = Coupon.create(COUPON_NAME, CouponStatus.AVAILABLE, LocalDateTime.now());
        var savedCoupon = createPort.create(coupon);

        // 쿠폰 히스토리 저장
        var couponHistory = CouponHistory.create(savedCoupon.getId(), request.outBoxId(), request.memberId(), LocalDateTime.now());
        historyCreatePort.create(couponHistory);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  후기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;지금까지 Polling Publisher 방법을 사용하여 트랜잭셔널 아웃박스 패턴에 대해 알아보았는데, 필자는 29CM 기술 블로그를 통해 트랜잭셔널 아웃박스 패턴을 적용하면서 무엇을 주의해야 하는지, MSA 환경에서 멱등성을 어떻게 보장해야 하는지, 정합성을 어떻게 보장해야 하는지 조금이나마 깊게 생각해 본 기회가 되었습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;그리고 Kafka는 너무 어렵다... 라는 것을 깨닭게 되었습니다ㅠㅠ 그렇지만 많은 기능을 제공해주고 있기 때문에 Kafka에 대해서 학습할 필요를 느꼈으며, 다음 포스팅에서는 Kafka Connect를 사용한 Transaction log tailing에 대해 학습해 보겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;위 블로그 내용은 안보더라도 아래 참고 링크에 있는 29CM 기술 블로그와 우와콘에서 소개하고 있는 아웃박스 패턴 적용 사례는 꼭 보시길 추천하겠습니다..!!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/kdg0209/my-real-trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/kdg0209/my-real-trip&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703746800470&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - kdg0209/my-real-trip&quot; data-og-description=&quot;Contribute to kdg0209/my-real-trip development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kdg0209/my-real-trip&quot; data-og-url=&quot;https://github.com/kdg0209/my-real-trip&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cIcfoJ/hyUTCwkE5x/szmMAHshrhLGem6QQ6PRu0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/kdg0209/my-real-trip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kdg0209/my-real-trip&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cIcfoJ/hyUTCwkE5x/szmMAHshrhLGem6QQ6PRu0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - kdg0209/my-real-trip&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to kdg0209/my-real-trip development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://www.daddyprogrammer.org/post/14068/database-migration-by-transactional-outbox/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.daddyprogrammer.org/post/14068/database-migration-by-transactional-outbox/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devocean.sk.com/blog/techBoardDetail.do?page=&amp;amp;boardType=undefined&amp;amp;query=&amp;amp;ID=165445&amp;amp;searchData=&amp;amp;subIndex=&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://devocean.sk.com/blog/techBoardDetail.do?page=&amp;amp;boardType=undefined&amp;amp;query=&amp;amp;ID=165445&amp;amp;searchData=&amp;amp;subIndex=&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@greg.shiny82/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%94%EB%84%90-%EC%95%84%EC%9B%83%EB%B0%95%EC%8A%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%8B%A4%EC%A0%9C-%EA%B5%AC%ED%98%84-%EC%82%AC%EB%A1%80-29cm-0f822fc23edb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@greg.shiny82/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%94%EB%84%90-%EC%95%84%EC%9B%83%EB%B0%95%EC%8A%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%8B%A4%EC%A0%9C-%EA%B5%AC%ED%98%84-%EC%82%AC%EB%A1%80-29cm-0f822fc23edb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@greg.shiny82/%EC%8B%A4%EB%AC%B4-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-apache-kafka-%ED%99%9C%EC%9A%A9-023d468f9182&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@greg.shiny82/%EC%8B%A4%EB%AC%B4-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-apache-kafka-%ED%99%9C%EC%9A%A9-023d468f9182&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WCwPSVu8mH8&amp;amp;t=710s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=WCwPSVu8mH8&amp;amp;t=710s&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA/SpringBoot</category>
      <category>polling publisher spring boot</category>
      <category>transactional outbox pattern spring boot</category>
      <category>트랜잭셔널 아웃박스 패턴 스프링 부트 예제</category>
      <category>트랜잭셔널 아웃박스 패턴 스프링부트</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/495</guid>
      <comments>https://kdg-is.tistory.com/entry/Transactional-Outbox-Pattern-with-Polling-Publisher#entry495comment</comments>
      <pubDate>Thu, 28 Dec 2023 16:03:02 +0900</pubDate>
    </item>
    <item>
      <title>MSA 환경에서 Transactional OutBox Pattern</title>
      <link>https://kdg-is.tistory.com/entry/MSA-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-OutBox-Pattern</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;natasa-grabovac-eRp165wNxro-unsplash.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EiiaY/btsBkl7M42p/KbXzovSKdmGSiNkjmEXUt0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EiiaY/btsBkl7M42p/KbXzovSKdmGSiNkjmEXUt0/img.jpg&quot; data-alt=&quot;By unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EiiaY/btsBkl7M42p/KbXzovSKdmGSiNkjmEXUt0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEiiaY%2FbtsBkl7M42p%2FKbXzovSKdmGSiNkjmEXUt0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1278&quot; data-filename=&quot;natasa-grabovac-eRp165wNxro-unsplash.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;By unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Transactional OutBox Pattern 이란?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;MSA 환경에서 데이터베이스의 상태가 변경되면 해당 트랜잭션과 함께 이벤트를 발행해야 하는 경우가 종종 발생하곤 합니다. 예를 들어 사용자가 회원가입을 완료한 경우 쿠폰을 발급하거나 이메일을 발송해야 하는 경우입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위와 같은 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이벤트를 발행하는 행위는 데이터베이스의 상태 변경과 원자적으로 실행되어야 합니다&lt;/b&gt;&lt;/span&gt;. 만약 데이터베이스에 회원가입 정보를 커밋한 뒤에 이벤트를 발행하면 이벤트가 어떠한 이유로 인해 오류가 발생하여 제대로 발행이 되지 않았다면? 데이터의 일관성이 깨질 우려가 있습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;간단한 코드로 조금 더 이해를 해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  코드로 살펴보기&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 코드는&amp;nbsp; 사용자의 회원가입 요청이 들어왔을 경우 한 트랜잭션 안에서 이벤트를 발송하고 있습니다. 그리고 이벤트 리스너에서는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;@TransactionalEventListener 어노테이션을 사용하여 회원가입 트랜잭션이 commit 되고 나서 이벤트가 발행되게 됩니다. 그렇다면 아래 코드에서 무엇인 문제가 될까요? 이벤트 리스너에서 이벤트를 받아 다른 애플리케이션에게 Rest Api로 호출하는 경우 네트워크에 문제가 발생하면 데이터베이스에는 데이터가 저장되지만 다른 애플리케이션에는 해당 데이터를 받을 수 없어 일관성이 깨지게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여기서 그럼 네트워크 문제가 발생했을 경우 Retry를 하면되지 않을까?라는 생각을 가질 수 있습니다. 하지만 Retry는 근본적인 해결책이 될 수 없습니다. 만약 Retry를 5번 했다고 했을 경우 다른 애플리케이션이 다운된 경우라면 N번을 요청해도 결과는 같을 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701500918747&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Service
@Transactional
@RequiredArgsConstructor
public class CreateMemberService implements CreateMemberUsecase {

    private final CreateMemberPort createMemberPort;
    private final ValidationMemberPort validationPort;
    private final ApplicationEventPublisher publisher;

    @Override
    public MemberCreateResponse create(MemberCreateRequest request) {
        validationPort.validateMemberId(request.id());
        validationPort.validateMemberEmail(request.email());

        var memberEmail = new MemberEmail(request.email());
        var member = Member.create(request.id(), request.password(), request.name(), request.nickName(), memberEmail);
        var entity = createMemberPort.createMember(member);

        // 이벤트를 발송시킨다.
        publisher.publishEvent(new MemberCreatedEvent(member.getId()));

        return new MemberCreateResponse(entity.converterPKToString());
    }
}

public record MemberCreatedEvent(String memberId) {

}

@Component
public class MemberEventListener {

    @TransactionalEventListener(classes = {MemberCreatedEvent.class})
    public void handle(MemberCreatedEvent event) {
        // event를 받아 다른 애플리케이션에게 REST API를 호출한다.
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Transactional OutBox Pattern 살펴보기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위와 같은 문제를 해결하기 위해서는 Transactional OutBox Pattern을 사용할 수 있는데, 데이터베이스의 상태가 변경될&amp;nbsp; 때마다 OutBox Table에 해당 이벤트도 함께 저장하는 것입니다. 그리고 별도의 프로세스가 OutBox Table을 주기적으로 읽어 저장된 이벤트를 이벤트 브로커에게 전달하는 방식입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;별도의 프로세스는 OutBox Table에 저장된 데이터를 가져와 작업을 수행하고 실패시 완료될 때까지 다시 시도할 수 있습니다. 따라서 이 방법은 적어도 한번 이상(at-least once) 메시지가 성공적으로 전송되었는지 보장할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-12-02 오후 4.21.34.png&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dagcEa/btsBjDns4pU/qNmkyYuodscKbVe1BVAWRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dagcEa/btsBjDns4pU/qNmkyYuodscKbVe1BVAWRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dagcEa/btsBjDns4pU/qNmkyYuodscKbVe1BVAWRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdagcEa%2FbtsBjDns4pU%2FqNmkyYuodscKbVe1BVAWRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2052&quot; height=&quot;846&quot; data-filename=&quot;스크린샷 2023-12-02 오후 4.21.34.png&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Message Relay&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Message Relay라는 프로세스가 있는데, 이 친구의 역할은 OutBox Table에 대한 변경 감지를 확인하는 친구입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;OutBox Table은 큐의 역할을 수행하며, Message Relay는 비동기적으로 데이터를 읽어 들입니다. 그리고 읽어 들인 데이터를 Message Broker에게 전달하게 됩니다. Message Relay를 구현하는 방법에는 Polling Publisher, Transaction log tailing, Kafka Connect 방법이 있다고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Polling Publisher&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;RDB를 사용하는 애플리케이션에서 OutBox table을 주기적으로 읽는 것입니다. 주기적으로 읽어 데이터를 처리하고 처리가 완료된 데이터는 table에서 삭제처리 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터베이스를 주기적으로 읽어야하기 때문에 데이터베이스에 부담이 가며, 폴링 주기를 잘 고려해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;또한 Polling Publisher만을 위한 인스턴스를 따로 구성해야 할 필요가 있습니다. 만약 따로 구성하지 않고 scale-out 되어 인스턴스가 N개 있는 경우라면 &lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: left;&quot;&gt;ShedLock을 사용할 필요가 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Transaction Log Tailing&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션에 의해 커밋된 정보들은 데이터베이스의 로그 항목으로 남는데, 트랜잭션 로그 마이너로 트랜잭션 로그를 읽어 변경분을 하나씩 메시지로 만들고, 메시지 브로커에 전달하는 방법입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;구현 난이도가 높아 Debezium, LinkedIn Databus와 같은 툴을 사용하는 경우가 많습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kafka Connect&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Kafka Connect도 Transaction Log Tailing 방법과 비슷하게 동작하는 것 같습니다. OutBox table의 트랜잭션 로그를 추적하여 커넥터에 의해 정의된 토픽에 메시지를 발행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;조금 더 자세한 설명은 다음 링크를 클릭하면 알 수 있습니다. &lt;a title=&quot;Kafka Connect 살펴보기&quot; href=&quot;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;주의점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;OutBox Pattern은 적어도 한번(&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;At-least Once&lt;/span&gt;) 이상은 메시지가 보내질 것이라고 확신할 수 있습니다. 그래서 Consumer에서는 중복된 메시지를 받을 수 있습니다. 그렇기 때문에 Consumer 입장에서는 멱등성이 보장되도록 애플리케이션을 구성해야 하며, 고유한 메시지 식별자를 함께 보내 이를 달성할 수 있다고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;장점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Two-Phase Commit(2PC) 방식을 사용하지 않기 때문에 성능 저하가 발생하지 않는다고 합니다. 이와 관련해서 구글링을 해보면 좋은 자료들을 찾아볼 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터베이스 커밋과 메시지 전송 모두 보장할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션이 전송한 순서대로 메시지 브로커로 전송됩니다. (전송한 순서는 결국 커밋 시점이므로)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;단점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;멱등성을 보장해야 합니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;후기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;지금까지 OutBox Pattern에 대해서 알아보았고 이후에는 Message Relay 방법으로 Polling Publisher와 Kafka Connct를 각각 구현해 보고 조금 더 깊게 다루도록 하겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Polling Publisher 방법으로 구현한 트랜잭셔널 아웃박스 패턴 살펴보기&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kdg-is.tistory.com/entry/Transactional-Outbox-Pattern-with-Polling-Publisher&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kdg-is.tistory.com/entry/Transactional-Outbox-Pattern-with-Polling-Publisher&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://happy-coding-day.tistory.com/entry/Message-Relay-%EB%A5%BC-Polling-publiser-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://happy-coding-day.tistory.com/entry/Message-Relay-%EB%A5%BC-Polling-publiser-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.gangnamunni.com/post/transactional-outbox/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.gangnamunni.com/post/transactional-outbox/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://krishnakrmahto.com/transactional-messaging-in-microservices&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://krishnakrmahto.com/transactional-messaging-in-microservices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA/SpringBoot</category>
      <category>transactional outbox pattern</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/494</guid>
      <comments>https://kdg-is.tistory.com/entry/MSA-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-OutBox-Pattern#entry494comment</comments>
      <pubDate>Sat, 2 Dec 2023 17:35:49 +0900</pubDate>
    </item>
    <item>
      <title>Space Based Architecture</title>
      <link>https://kdg-is.tistory.com/entry/Space-Based-Architecture</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Space Based Architecture&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;공간 기반 아키텍처는 확장성과 탄력성, 동시성의 문제를 해결하기 위해 등장한 아키텍처입니다. 많은 웹 기반 애플리케이션은 보통 웹 서버(NGINX), 애플리케이션 서버, 데이터베이스 서버로 구성되어 있으며 하나의 요청은 이 순서대로 도달하게 됩니다. 이때 사용자가 많지 않으면 별 문제가 발생하지 않지만 사용자가 증가함에 따라 병목 현상이 발생하게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;병목 현상의 일반적인 해결 방법은 웹 서버를 확장하고 이후 애플리케이션 서버, 데이터베이스 서버 순으로 확장할 수 있지만 뒤로 갈수록 확장하는데 비용도 많이 들고 점차 복잡해져 갑니다. 그리고 최종적으로 데이터베이스의 동시 처리 가능한 트랜잭션 수가 최종 제약조건이 되는 경우가 많습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 9.23.51.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiQeaI/btsuPNc9u6N/KkdtsJD4m8rmzzwNp4tUD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiQeaI/btsuPNc9u6N/KkdtsJD4m8rmzzwNp4tUD1/img.png&quot; data-alt=&quot;확장 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiQeaI/btsuPNc9u6N/KkdtsJD4m8rmzzwNp4tUD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeiQeaI%2FbtsuPNc9u6N%2FKkdtsJD4m8rmzzwNp4tUD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;986&quot; height=&quot;285&quot; data-filename=&quot;스크린샷 2023-09-20 오후 9.23.51.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;확장 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;토폴로지&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;공간 기반 아키텍처라는 명칭은 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;튜플 공간&lt;/b&gt;&lt;/span&gt;에서 유래되었습니다. &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;튜플 공간은 공유 메모리를 통해 통신하는 다중 병렬 프로세스를 사용하는 기술입니다. &lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;시스템에서 동기 제약조건인 데이터베이스를 없애는 대신 복제된 인메모리 데이터 그리드를 활용하면 확장성, 탄력성, 성능을 높일 수 있습니다. 애플리케이션 데이터는 메모리에 둔 상태로 모든 활성 애플리케이션에 데이터를 복제합니다. 애플리케이션은 데이터를 업데이트할 때 큐에 메시지를 보내서 비동기로 데이터베이스를 업데이트합니다. 또한 애플리케이션은 사용자의 수에 따라 가변적으로 시작/종료할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Space Based Architecture의 구조&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;공간 기반 아키텍처의 구조는 애플리케이션 코드가 구현된 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처리 장치&lt;/b&gt;&lt;/span&gt;, 처리 장치를 관리/조정하는 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가상 미들웨어&lt;/span&gt;&lt;/b&gt;, 업데이트된 데이터를 데이터베이스에 비동기로 전송하는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;데이터 펌프&lt;/b&gt;&lt;/span&gt;, 데이터 펌프에서 데이터를 받아 업데이트를 수행하는 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터 라이터&lt;/span&gt;&lt;/b&gt;, 처리 장치가 시작되자마자 데이터베이스의 데이터를 읽어 전달하는 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터 리더&lt;/span&gt;&lt;/b&gt;로 구성되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 9.59.54.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cn31Q/btsuIzT4zhS/vcMWotLazkcCNxauKuPI60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cn31Q/btsuIzT4zhS/vcMWotLazkcCNxauKuPI60/img.png&quot; data-alt=&quot;Space Based Architecture 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cn31Q/btsuIzT4zhS/vcMWotLazkcCNxauKuPI60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCn31Q%2FbtsuIzT4zhS%2FvcMWotLazkcCNxauKuPI60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1018&quot; height=&quot;523&quot; data-filename=&quot;스크린샷 2023-09-20 오후 9.59.54.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Space Based Architecture 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  처리 장치&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;처리 장치는 애플리케이션의 로직을 가지고 있습니다. 보통 웹 기반 컴포넌트와 백엔드 비즈니스 로직이 포함되어 있고 애플리케이션의 크기에 따라 레이어드 구조 및 마이크로 서비스 구조로 구성할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션 로직 외에도 헤이즐캐스트, 아파치 이그나이트 등의 제품에 있는 인메모리 데이터 그리드 및 복제 엔진도 포함됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;  가상 미들웨어&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;가상 미들웨어는 아키텍처 내부에서 데이터 동기화 및 요청 처리의 다양한 부분을 제어하는 인프라를 담당합니다. 여기에는 메시징 그리드, 데이터 그리드, 처리 그리드, 배포 관리자가 포함됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;메시징 그리드&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;메시징 그리드는 입력 요청과 세션 상태를 관리합니다. 가상 미들웨어에 요청이 들어오면 메시징 그리드는 어떤 활성 처리 장치가 요청을 받아 처리할지 결정하여 해당 처리 장치로 요청을 전달합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.33.09.png&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqbtfH/btsuWXTx3D1/2w5BmKBWLfvxNEsMeTBLo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqbtfH/btsuWXTx3D1/2w5BmKBWLfvxNEsMeTBLo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqbtfH/btsuWXTx3D1/2w5BmKBWLfvxNEsMeTBLo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqbtfH%2FbtsuWXTx3D1%2F2w5BmKBWLfvxNEsMeTBLo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;430&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.33.09.png&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터 그리드&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 그리드는 아키텍처에서 가장 중요한 부분입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 그리드는 각 처리 장치의 데이터 복제 엔진과 상호 작용하여 데이터 업데이트 이벤트가 발생할 때 처리 장치 간의 데이터 복제를 관리합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;메시징 그리드는 가용한 모든 처리 장치에 요청을 전달할 수 있으므로 각 처리 장치는 자신의 인메모리 데이터 그리드에 정확히 동일한 데이터를 가지고 있어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.32.45.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0s1jW/btsu18zZKqj/BydeJOsnxvu4J04G2nOHw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0s1jW/btsu18zZKqj/BydeJOsnxvu4J04G2nOHw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0s1jW/btsu18zZKqj/BydeJOsnxvu4J04G2nOHw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0s1jW%2Fbtsu18zZKqj%2FBydeJOsnxvu4J04G2nOHw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;996&quot; height=&quot;419&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.32.45.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;처리 그리드&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;처리 그리드는 애플리케이션의 일부를 처리하는 여러 처리 장치가 있을 때 분산 요청 처리를 관리하는 역할을 수행합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예시로 주문 처리 장치와 고객 처리 장치 간의 조율이 필요한 요청이 들어온 경우 두 처리 장치 간의 요청을 중재하고 조율하는 역할을 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;배포 관리자&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;배포 관리자는 부하 조건에 따라 처리 장치 인스턴스를 동적으로 시작/종료하는 컴포넌트입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;  데이터 펌프&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 펌프는 데이터를 다른 프로세스에 보내 데이터베이스를 업데이트 하는 장치입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;공간 기반 아키텍처에서는 처리 장치가 데이터를 데이터베이스에서 직접 읽고 쓰지 않으므로 데이터 펌프는 필수입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 펌프는 항상 비동기로 동작하면서 메모리 캐시와 데이터베이스의 최종 일관성을 실현합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;공간 기반 아키텍처에서 메시징은 데이터 펌프를 구현하는데 효과적인 방법이고, 메시징은 비동기 통신을 지원하고 전달을 보장하며, FIFO 큐를 통해 순서도 유지할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;  데이터 라이터&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 라이터는 데이터 펌프에서 메시지를 받아 그에 맞는 데이터베이스를 업데이트하는 컴포넌트입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 라이터는 도메인마다 또는 처리 장치 클래스마다 자체 전용 데이터 라이터를 가질 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.50.38.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lyC3X/btsuJbyGRvk/rq979UiKNJBk4UFHjJ1js0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lyC3X/btsuJbyGRvk/rq979UiKNJBk4UFHjJ1js0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lyC3X/btsuJbyGRvk/rq979UiKNJBk4UFHjJ1js0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlyC3X%2FbtsuJbyGRvk%2Frq979UiKNJBk4UFHjJ1js0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1046&quot; height=&quot;362&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.50.38.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;  데이터 리더&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터 리더는 데이터베이스에서 데이터를 읽어 데이터 펌프를 통해 처리 장치로 실어 나르는 컴포넌트입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터 리더가 동작되는 경우&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;동일한 이름의 캐시를 가진 모든 처리 장치 인스턴스가 실패하는 경우입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;동일한 이름의 캐시 안에서 모든 처리 장치를 재배포하는 경우입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;복제 캐시에 들어있지 않은 아카이브 데이터를 조회하는 경우입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.57.36.png&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mb7JA/btsuZ1BonwQ/BXJeCnwM6cTmeEoPW252O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mb7JA/btsuZ1BonwQ/BXJeCnwM6cTmeEoPW252O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mb7JA/btsuZ1BonwQ/BXJeCnwM6cTmeEoPW252O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmb7JA%2FbtsuZ1BonwQ%2FBXJeCnwM6cTmeEoPW252O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1048&quot; height=&quot;397&quot; data-filename=&quot;스크린샷 2023-09-20 오후 10.57.36.png&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; text-align: start;&quot;&gt;데이터 충돌&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이름이 동일한 캐시가 포함된 애플리케이션에서 업데이트가 발생하는 active/active 상태에서 복제 캐시를 사용하면 복제 레이턴시 때문에 데이터 충돌이 발생할 수 있습니다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;상황&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;노트북의 재고는 100개입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션 A는 노트북의 재고 캐시를 90(10개 판매)로 업데이트합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;캐시를 복제하는 동안 애플리케이션 B는 노트북의 재고 캐시를 85(15개 판매)로 업데이트합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션 A의 데이터를 애플리케이션 B로 복제했기 때문에 애플리케이션 B의 노트북 재고 캐시는 90개입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션 B의 데이터를 애플리케이션 A로 복제했기 때문에 애플리케이션 A의 노트북 재고 캐시는 85개입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;애플리케이션 A, B 두 캐시 값이 동일하지 않아 동기화가 어긋납니다. (노트북 재고는 75개가 되어야 함)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; text-align: start;&quot;&gt;복제 캐시 vs 분산 캐시&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;공간 기반 아키텍처는 캐시 기술을 사용하여 애플리케이션 트랜잭션을 처리하고 데이터베이스에서 직접 읽기/쓰기를 할 필요가 없으므로 확장성, 탄력성, 성능이 좋습니다. 캐시 기술에는 복제 캐시와 분산 캐시를 사용할 수 있습니다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;또한 캐시 모델을 선정할 때 아래 두 모델을 모두 적용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  복제 캐시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;복제 캐시를 사용하면 각 처리 장치에서 이름이 동일한 캐시를 사용하는 모든 처리 장치 간에 동기화되는 자체 인메모리 데이터 그리드를 가지고 있습니다.(헤이즐캐스트)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;한 처리 장치에서 캐시가 업데이트되면 다른 처리 장치도 새로운 데이터로 자동 업데이트되는 구조입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;복제 캐시는 속도가 매우 빠르고 높은 수준의 내고장성을 지원하며, 중앙 서버에 캐시를 가지고 있는 형태가 아니므로 단일 장애지점이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;복제 캐시는 공간 기반 아키텍처의 표준 캐시 모델이지만 캐시의 크기가 큰 경우나 캐시 데이터가 너무 빈번히 업데이트되는 등 복제 캐시를 사용할 수 없는 경우도 있습니다. 또한 복제 캐시의 공간은 애플리케이션의 메모리에 따라 달라집니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;캐시 데이터 업데이트율이 매우 높은 경우 모든 처리 장치 인스턴스에서 데이터 일관성이 보장되도록 업데이트되어야 하지만 데이터 그리드가 이 속도를 못 따라갈 수 있습니다. 따라서 이럴 때 분산 캐시를 사용해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.25.00.png&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLSQvD/btsuZ0ic2jf/7TFOXmZnkNetfGkDZsjrn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLSQvD/btsuZ0ic2jf/7TFOXmZnkNetfGkDZsjrn0/img.png&quot; data-alt=&quot;복제 캐시 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLSQvD/btsuZ0ic2jf/7TFOXmZnkNetfGkDZsjrn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLSQvD%2FbtsuZ0ic2jf%2F7TFOXmZnkNetfGkDZsjrn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1972&quot; height=&quot;514&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.25.00.png&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복제 캐시 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;  분산 캐시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;분산 캐시를 사용하면 중앙 캐시를 가지고 있는 전용 외부 서비스(Redis)가 필요합니다. 처리 장치는 인메모리 데이터 그리드를 가지고 있는 대신 전용 프로토콜을 통해 전용 캐시 서버에 접근합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 데이터가 한 곳에 있으므로 데이터의 일관성을 보장할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;캐시 데이터를 원격에서 가져와야 하므로 복제 캐시보다는 성능이 낮고 레이턴시가 증가합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터를 가지고 있는 캐시 서버가 다운되면 모든 처리 장치의 작동이 중단되므로 문제가 있지만 클러스트링을 사용하면 괜찮지 않을까 생각합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.35.38.png&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqQ38u/btsu0q14HtF/uTFycxKzmQM5JMoo0C9kT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqQ38u/btsu0q14HtF/uTFycxKzmQM5JMoo0C9kT0/img.png&quot; data-alt=&quot;분산 캐시 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqQ38u/btsu0q14HtF/uTFycxKzmQM5JMoo0C9kT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqQ38u%2Fbtsu0q14HtF%2FuTFycxKzmQM5JMoo0C9kT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2004&quot; height=&quot;636&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.35.38.png&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분산 캐시 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; text-align: start;&quot;&gt;니어 캐시&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;니어 캐시는 분산 캐시와 처리 장치의 인메모리 데이터 그리드를 합친 하이브리드 캐시 모델입니다. &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;분산 캐시는 풀 백킹 캐시&lt;/b&gt;&lt;/span&gt;이고 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 처리 장치에 포함된 인메모리 데이터 그리드는 프런트 캐시&lt;/span&gt;&lt;/b&gt;라 합니다. 프런트 캐시는 항상 풀 백킹 캐시보다 작은 서브세트를 담고 있고, 방출 정책을 통해 예전의 항목을 삭제합니다.(MRU, MFU, RR 등의 알고리즘)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프런트 캐시는 항상 풀 백킹 캐시와 동기화되지만 각 처리 장치에 포함된 프런트 캐시는 동일한 데이터를 공유하는 다른 처리 장치와 동기화 되지 않습니다. 따라서 동일한 데이터 콘텍스트를 공유하는 여러 처리 장치가 동일하지 않은 데이터를 각자의 프런트 캐시로 소유하고 있을 수 있습니다. 따라서 데이터 일관성이 떨어질 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.48.06.png&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lSvKP/btsuZ19gyMP/vvBFeXFnFtMKzLeL7ypfO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lSvKP/btsuZ19gyMP/vvBFeXFnFtMKzLeL7ypfO0/img.png&quot; data-alt=&quot;니어 캐시 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lSvKP/btsuZ19gyMP/vvBFeXFnFtMKzLeL7ypfO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlSvKP%2FbtsuZ19gyMP%2FvvBFeXFnFtMKzLeL7ypfO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1988&quot; height=&quot;768&quot; data-filename=&quot;스크린샷 2023-09-21 오전 9.48.06.png&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;니어 캐시 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333;&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/the-pragmatic-tech-review/understanding-space-based-architecture-15281953c24c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/the-pragmatic-tech-review/understanding-space-based-architecture-15281953c24c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/nerd-for-tech/is-space-based-pattern-only-for-cloud-based-hosted-5a633eb74c2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/nerd-for-tech/is-space-based-pattern-only-for-cloud-based-hosted-5a633eb74c2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://umairsaeed.com/space-based-architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://umairsaeed.com/space-based-architecture/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>space based architecture</category>
      <category>공간 기반 아키텍처</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/492</guid>
      <comments>https://kdg-is.tistory.com/entry/Space-Based-Architecture#entry492comment</comments>
      <pubDate>Thu, 21 Sep 2023 09:51:08 +0900</pubDate>
    </item>
    <item>
      <title>Event Driven Architecture</title>
      <link>https://kdg-is.tistory.com/entry/Event-Driven-Architecture</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rebekah-haddock-h9-pOEv3QZk-unsplash.jpg&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Wb7K/btst84fA8l9/mlQMwJUuopXrRJF7hh0aoK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Wb7K/btst84fA8l9/mlQMwJUuopXrRJF7hh0aoK/img.jpg&quot; data-alt=&quot;By unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Wb7K/btst84fA8l9/mlQMwJUuopXrRJF7hh0aoK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Wb7K%2Fbtst84fA8l9%2FmlQMwJUuopXrRJF7hh0aoK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6000&quot; height=&quot;4000&quot; data-filename=&quot;rebekah-haddock-h9-pOEv3QZk-unsplash.jpg&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;By unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Event&amp;nbsp;Driven&amp;nbsp;Architecture&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 기반 아키텍처는 확장성이 뛰어난 고성능 애플리케이션 개발에 널리 쓰이는 비동기 분산 아키텍처입니다. 분산 시스템에서 이벤트를 발행하고 수신자에게 이벤트를 전송하는 구조로 수신자는 해당 이벤트를 처리하는 방식의 아키텍처입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;토폴리지&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: 'Noto Serif KR';&quot;&gt;이벤트 기반 아키텍처의 다양한 하위 패턴을 알아보기 위해서는 토폴리지를 이해하는 것이 중요합니다. 이벤트 기반 아키텍처는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;중재자 토폴리지&lt;/b&gt;, &lt;b&gt;브로커 토폴리지&lt;/b&gt;&lt;/span&gt; 기반으로 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;브로커 토폴리지&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브로커 토폴리지에서 메시지는 메시지 브로커(RabbitMQ, Kafka)를 통해 브로드캐스팅되는 식으로 이벤트 프로세스 컴포넌트에게 분산되어 흘러갑니다. 브로커 토폴리지는 이벤트 처리 흐름이 단순하고, 중앙에서 이벤트를 조율할 필요성일 없을 때 유용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브로커 토폴리지는 네 가지 기본 아키텍처 컴포넌트를 가집니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;시작 이벤트&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트는 단순한 이벤트든 복잡한 이벤트든 &lt;b&gt;전체 이벤트 흐름을 개시하는 이벤트&lt;/b&gt;를 말합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트는 이벤트 브로커의 채널로 전송됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이벤트 브로커&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트 또는 가공된 이벤트를 보관하고 있는 메시지 브로커입니다.(RabbitMQ, Kafka)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 브로커를 subscribe하고 있는 이벤트 프로세서에게 메시지를 발행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이벤트 프로세스&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 브로커로부터 메시지를 가져와 자신의 역할에 맞는 임무를 수행합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;자신이 맡은 임무를 수행한 뒤 처리 이벤트를 발행하여 시스템의 나머지 부분에 자신이 한 일을 비동기로 알립니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;처리 이벤트&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;다른 이벤트 프로세스 처리 이벤트 메시지 브로커를 구독하고 있다가 이벤트가 들어오면 그에 맞는 작업을 수행한 뒤 새로운 처리 이벤트를 발행합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 2.21.32.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7kuBH/btsuenZJtAL/UmElYRcjXrSQo2msV3t0p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7kuBH/btsuenZJtAL/UmElYRcjXrSQo2msV3t0p0/img.png&quot; data-alt=&quot;대략적인 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7kuBH/btsuenZJtAL/UmElYRcjXrSQo2msV3t0p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7kuBH%2FbtsuenZJtAL%2FUmElYRcjXrSQo2msV3t0p0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;624&quot; data-filename=&quot;스크린샷 2023-09-16 오후 2.21.32.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대략적인 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브로커 토폴로지에서는 다른 이벤트 프로세스의 관심 여부와 무관하게 각 이벤트 프로세스가 자신이 한 일을 모두에게 알리는 게 항상 바람직하다고 합니다. 그래야 나중에 이벤트를 처리하는 과정에서 기능추가가 필요하게 되더라도 아키텍처를 쉽게 확장할 수 있다고 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  예시&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 예제 그림을 보면 각 서비스는 분리되어 있고, 독립적으로 움직입니다. 브로커 토폴리지는 마치 릴레이 경주와 같다고 생각하면 이해하기 쉽습니다. 각 주자는 이벤트라는 바통을 들고 자신의 역할에 맡는 임무를 다합니다. (500m 달리기) 그리고 임무를 다하였으면 다음 주자에게 바통을 넘겨주는 식으로 마지막 주자가 결승전을 통과할 때까지 정해진 순서에 따라 진행됩니다. 그리고 경주를 마친 주자는 다음 질주를 위해 대기하게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브로커 토폴로지도 이와 비슷하게 동작을 하는데 각 서비스는 처리 이벤트를 발행 후 해당 이벤트에 더 이상 관여하지 않고 다른 이벤트에 반응할 준비를 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 2.43.48.png&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HPFe6/btst6kpFpwJ/rQGKkZO7Jd7AkdzZhf8241/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HPFe6/btst6kpFpwJ/rQGKkZO7Jd7AkdzZhf8241/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HPFe6/btst6kpFpwJ/rQGKkZO7Jd7AkdzZhf8241/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHPFe6%2Fbtst6kpFpwJ%2FrQGKkZO7Jd7AkdzZhf8241%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;886&quot; data-filename=&quot;스크린샷 2023-09-16 오후 2.43.48.png&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  브로커 토폴로지의 장단점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 이벤트 프로세스의 커플링이 낮아집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;확장성이 높습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트와 연관된 전체 워크플로를 제어할 수 없습니다. 따라서 각 서비스에서 실제 트랜잭션이 언제 끝났는지 알 수 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;에러 처리가 어렵습니다. 비즈니스 트랜잭션을 관리/통제하는 중재자가 없으므로 처리를 실패해도 다른 서비스에서는 해당 사실을 알 수 없습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;비지니스 트랜잭션을 재시작하는 기능도 브로커 토폴로지에서는 지원되지 않습니다. 처음 시작 이벤트를 처리할 때부터 이미 다른 작업이 비동기로 수행된 터라 시작 이벤트를 다시 넣는 것은 불가능 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;중재자 토폴리지&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;중재자 토폴리지는 위에서 살펴본 브로커 토폴리지의 단점을 일부 보완할 수 있습니다.&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; 여러 이벤트 프로세스 간의 조율이 필요한 시작 이벤트에 대해 워크플로를 관리/제어하는 이벤트 중재자가 핵심&lt;/b&gt;&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;중재자 토폴리지는 다섯가지 기본 아키텍처 컴포넌트를 가집니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;시작 이벤트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트는 단순한 이벤트든 복잡한 이벤트든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;전체 이벤트 흐름을 개시하는 이벤트&lt;/b&gt;를 말합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;시작 이벤트는 이벤트 브로커의 채널로 전송됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이벤트 중재자&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 중재자는 이벤트 처리에 관한 단계 정보만 가지고 있으므로 점대점 메시징으로 각각 이벤트 채널로 전달되는 이벤트를 생성합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이벤트 채널&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 중재자와 이벤트 프로세스가 메시지를 주고 받기 위한 의사소통 창구입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 중재자는 이벤트 채널을 통해 이벤트 프로세스에게 이벤트를 전달하는 창구입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로세스는 자신 임무를 완료했다는 것을 알리기 위해 이벤트 채널을 통해 이벤트 중재자에게 메시지를 전달하는 창구입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;이벤트 프로세스&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로세스는 이벤트 채널로부터 이벤트를 받아 처리한 다음 이벤트 채널로 작업을 완료했다는 응답을 보냅니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  일반적인 아키텍처&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;아래 그림은 가장 일반적인? 중재가 토폴로지 입니다. 이벤트 중재자는 처리하는 이벤트의 특성과 복잡도에 따라 다양하게 구현할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.27.47.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGM9AB/btst7RgkU4l/iJoG6FlklUWJi8EWe22Kw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGM9AB/btst7RgkU4l/iJoG6FlklUWJi8EWe22Kw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGM9AB/btst7RgkU4l/iJoG6FlklUWJi8EWe22Kw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGM9AB%2Fbtst7RgkU4l%2FiJoG6FlklUWJi8EWe22Kw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2064&quot; height=&quot;644&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.27.47.png&quot; data-origin-width=&quot;2064&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  복잡도에 따른 아키텍처&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&lt;b&gt;이벤트 복잡도를 한 가지 기준으로 평가하는 경우는 거의 없으므로 알기 쉽게 단순함, 어려움, 복잡함 정보로 분류한 뒤 모든 이벤트가 항상 단순한 중개자를 거치도록 하는 것이 좋습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.35.45.png&quot; data-origin-width=&quot;2062&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qBAmR/btsudh6KseI/lkIuQDQuU4Ht6d8dOdwSKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qBAmR/btsudh6KseI/lkIuQDQuU4Ht6d8dOdwSKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qBAmR/btsudh6KseI/lkIuQDQuU4Ht6d8dOdwSKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqBAmR%2Fbtsudh6KseI%2FlkIuQDQuU4Ht6d8dOdwSKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2062&quot; height=&quot;822&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.35.45.png&quot; data-origin-width=&quot;2062&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  예시&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 예시는 위의 브로커 토폴리지에서 사용된 예시입니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1단계&lt;/b&gt;&lt;/span&gt;에서는 접수를 생성하기 위해 이벤트 중재자는 접수 서비스의 이벤트 채널에 이벤트를 발행하고, 접수 서비스는 임무를 완료한 뒤 이벤트 중재자에게 이 사실을 알립니다. 그리고 이벤트 중재자는 응답을 받아 2단계로 넘어가게 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.53.38.png&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6fl5/btsufltOxSL/u5b7e7oRNR8dXRS2fl7k0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6fl5/btsufltOxSL/u5b7e7oRNR8dXRS2fl7k0K/img.png&quot; data-alt=&quot;1단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6fl5/btsufltOxSL/u5b7e7oRNR8dXRS2fl7k0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6fl5%2FbtsufltOxSL%2Fu5b7e7oRNR8dXRS2fl7k0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2070&quot; height=&quot;776&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.53.38.png&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2단계&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;에서는 결제에게 이벤트를 보내고 응답을 받으면 3단계로 넘어가게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.53.05.png&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3ytew/btsueNc0P9W/GbZJ8y6xmmbNkkIzKwjV10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3ytew/btsueNc0P9W/GbZJ8y6xmmbNkkIzKwjV10/img.png&quot; data-alt=&quot;2단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3ytew/btsueNc0P9W/GbZJ8y6xmmbNkkIzKwjV10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3ytew%2FbtsueNc0P9W%2FGbZJ8y6xmmbNkkIzKwjV10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2070&quot; height=&quot;786&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.53.05.png&quot; data-origin-width=&quot;2070&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3단계&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;에서는 똑같이 이벤트 중재자가 알림과 라이더 이벤트를 발행하고 각 서비스는 이에 대해 임무를 완료한 뒤 이벤트 중재자에게 응답을 하게됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.54.10.png&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cA3J1d/btst6pRZEgr/mGJxyKZeuRea6l2Sli4CMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cA3J1d/btst6pRZEgr/mGJxyKZeuRea6l2Sli4CMK/img.png&quot; data-alt=&quot;3단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA3J1d/btst6pRZEgr/mGJxyKZeuRea6l2Sli4CMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA3J1d%2Fbtst6pRZEgr%2FmGJxyKZeuRea6l2Sli4CMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2068&quot; height=&quot;764&quot; data-filename=&quot;스크린샷 2023-09-16 오후 3.54.10.png&quot; data-origin-width=&quot;2068&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  중재자 토폴로지의 장단점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 중재자는 브로커 토폴로지와는 다르게 전체 워크플로에 대해 잘 알고 있고 통제가 가능합니다. 따라서 이벤트 상태에 대해 에러처리, 복구, 재시작을 할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로세스는 브로커 토폴리지와 마찬가지로 확장할 수 있지만, 이벤트 중재자도 함께 확장해야 하므로 이벤트 처리 흐름에 병목 지점이 생길 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 중재자는 이벤트 처리를 중간에서 제어해야 하므로 이벤트 프로세스가 상대적으로 더 많이 커플링되어 성능은 브로커 토폴리지에 비해 좋지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  정리&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브로커 토폴리지냐, 중재자 토폴리지냐, 결국 워크플로 제어와 에러처리 기능이 우선인가 아니면 고성능과 확장성이 더 우선인가에 대한 트레이드오프를 해야합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;에러처리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;리액티브 아키텍처의 워크플로 이벤트 패턴은 비동기 워크플로에서 에러처리 문제를 해결하는 한 가지 방법입니다. &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;워크플로 이벤트 패턴은 워크플로 대리자를 통해 위임, 봉쇄, 수리 작업을 합니다.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로세스는 이벤트 채널을 통해 데이터를 이벤트 컨슈머에게 비동기로 전송하고, 이벤트 컨슈머가 데이터를 처리하는 도중 에라가 발생하면 즉시 에러를 워크플로 프로세스에게 위임한 뒤 이벤트 채널에 있는 다음 메시지로 넘어갑니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 에러가 발생하더라도 다음 메시지를 처리할 수 있으므로 전체 응답성은 영향을 받지 않습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;에러를 수신한 워크플로 프로세스는 메시지의 잘못된 내용을 파악한 뒤 원데이터를 변경해서 긴급 조치한 뒤 원래 큐로 돌려 보냅니다. 그리고 이벤트 컨슈머는 이 메시지를 새로운 메시지로 간주하여 재처리를 하게 됩니다. 하지만 다시 에러가 발생하면 다시 워크플로 프로세스가 받게되고 이번에는 문제를 파악할 수 없게된다면 대시보드라고 부르는 애플리케이션(이메일)으로 보내고 업무 담당가 처리할 수 있도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.18.14.png&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WNKLg/btsueMFbn7Y/5zybZm1oRvkpCtVGPrVIyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WNKLg/btsueMFbn7Y/5zybZm1oRvkpCtVGPrVIyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WNKLg/btsueMFbn7Y/5zybZm1oRvkpCtVGPrVIyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWNKLg%2FbtsueMFbn7Y%2F5zybZm1oRvkpCtVGPrVIyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1976&quot; height=&quot;566&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.18.14.png&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터 소실 방지&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;비동기 통신을 할 때 데이터 소실은 언제나 중요한 관심사입니다. 하지만 이벤트 기반 아키텍처는 데이터가 소실될 만한 곳이 많습니다. 여기서 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;데이터 소실이란 메시지가 도중에 삭제되거나 최종 목적지에 도착하지 못한 상태를 말합니다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.28.20.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBzu4r/btst6loAD9L/8RA3UQ6dNO5mv8AHqQsqa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBzu4r/btst6loAD9L/8RA3UQ6dNO5mv8AHqQsqa1/img.png&quot; data-alt=&quot;데이터 소실 지점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBzu4r/btst6loAD9L/8RA3UQ6dNO5mv8AHqQsqa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBzu4r%2Fbtst6loAD9L%2F8RA3UQ6dNO5mv8AHqQsqa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1872&quot; height=&quot;200&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.28.20.png&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 소실 지점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;소실 1 : 메시지가 큐로 전달되지 않거나 전달되어도 다음 프로세스가 메시지를 가져가기전에 브로커가 다운되는 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;동기 전송과 퍼시스턴트 메시지 큐를 이용해서 해결할 수 있다고 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;소실 2 : 프로세스 B가 큐에서 메시지를 꺼내 이벤트를 처리하기 전에 장애가 발생하는 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트 확인응답 모드라는 메시징 기술을 사용해서 해결가능 합니다. 원래 메시지는 큐에서 빠져나가는 즉시 삭제되는데(자동 확인응답 모드), 클라이언트의 확인응답 모드는 메시지를 큐에 보관한 채 다른 컨슈머가 메시지를 읽을 수 없게 클라이언트 ID를 메시지에 부착합니다. 따라서 프로세스 B가 잘못되어도 메시지는 큐에 남아있으므로 해결할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;소실 3 : 데이터 에러로 인해 데이터베이스에 저장할 수 없는 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;최종 참여자 지원을 사용하여 메시지 처리가 끝나 데이터베이스에 저장됐음을 확인합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;요청 - 응답&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;지금까지 이벤트 컨슈머의 즉시 응답이 필요하지 않은 비동기 요청만을 살펴보았습니다. 하지만 각 프로세스 간에 동기적인 통신이 필요한 경우는 어떻게 대처할 수 있을까요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 기반 아키텍처는 동기 통신이 필요한 경우 요청 - 응답 메시징 방식으로 수행할 수 있습니다. 요청 - 방식 메시징 내부의 각 이벤트 채널은 요청 큐, 응답 큐로 구성됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  첫번째 방법 : 메시지 헤더에 상관 ID 사용&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로듀서는 요청큐에 메시지를 보내고, 고유한 메시지 ID를 기록합니다. 아직 상관 ID(CID)는 NULL 입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 2&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 프로듀서는 메시지 필터로 응답 큐를 차단 대기합니다. 이때 응답 큐에는 상관 ID 124와 일치하는 것이 없으므로 아직까지 대기하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 3&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 컨슈머 메시지(ID 124)를 받아 처리합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 4&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 컨슈머는 응답 메시지를 생성하고 메시지 헤더의 상관 ID를 원메시지 ID로 설정합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 5&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이벤트 컨슈머는 새 메시지를 응답 큐로 보냅니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;과정 6&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2단계의 메시지 셀렉터와 상관 ID가 일치하므로 이벤트 프로듀서는 메시지를 수신합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.59.51.png&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNIkAH/btsuh4yo14L/2KqLbxCMYhvKdumRgDdISk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNIkAH/btsuh4yo14L/2KqLbxCMYhvKdumRgDdISk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNIkAH/btsuh4yo14L/2KqLbxCMYhvKdumRgDdISk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNIkAH%2Fbtsuh4yo14L%2F2KqLbxCMYhvKdumRgDdISk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1950&quot; height=&quot;610&quot; data-filename=&quot;스크린샷 2023-09-16 오후 4.59.51.png&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://pub.towardsai.net/software-engineering-baa4e7a8015c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pub.towardsai.net/software-engineering-baa4e7a8015c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/microservicegeeks/introduction-to-event-driven-architecture-e94ef442d824&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/microservicegeeks/introduction-to-event-driven-architecture-e94ef442d824&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.codeparrot.ai/testing-event-driven-architecture-20636f3b98d1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.codeparrot.ai/testing-event-driven-architecture-20636f3b98d1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@borab/%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EA%B3%BC-Event-Driven-Architecture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@borab/%EB%B6%84%EC%82%B0-%ED%99%98%EA%B2%BD%EA%B3%BC-Event-Driven-Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>Event Driven Architecture</category>
      <category>이벤트 기반 아키텍처</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/491</guid>
      <comments>https://kdg-is.tistory.com/entry/Event-Driven-Architecture#entry491comment</comments>
      <pubDate>Sat, 16 Sep 2023 17:17:51 +0900</pubDate>
    </item>
    <item>
      <title>Service Based Architecture</title>
      <link>https://kdg-is.tistory.com/entry/Service-Based-Architecture</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Service Based Architecture&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 기반 아키텍처는 각각 따로 배포된 유저 인터페이스와 원격 서비스 그리고 데이터베이스로 이루어진 대규모 분산 아키텍처입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모놀리식 기반의 아키텍처를 가지고 있는 조직에서 마이크로서비스 아키텍처로 전환활 때 넘어가야 할 산들이 많은데(코드 분리, 데이터베이스 분리, 모니터링, 로깅, 분산 트랜잭션) 서비스 기반 아키텍처에서는 많은 변경이 필요 없다고 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 기반 아키텍처에서 서비스는 큼지막한 단위로 분리해 별도로 배포하는 애플리케이션의 일부입니다. 이 아키텍처에서 도메인 서비스는 각각 단일 인스턴스로 배포하며, 확장성, 내고장성, 처리량 또는 요구사항에 따라 인스턴스를 여러 개 둘 수 있습니다. 또한, 서비스마다 데이터베이스를 가질 수 있거나, 중앙 공유 데이터베이스를 사용할 수 있습니다. 따라서 기본 모놀리식 아키텍처에서 동일한 방식으로 SQL 쿼리와 조인을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 그림을 보면 Service A는 데이터베이스가 독립적으로 존재하며, Service B, C는 데이터베이스를 공유하여 사용하고 있습니다. 이때 &lt;b&gt;중요한 점은 각 데이터베이스에 있는 도메인 데이터를 다른 도메인의 서비스가 필요로 하지 않도록 설계하는 것입니다. 그렇게 해야 도메인 서비스 간 상호 통신을 방지하고(서비스 기반 아키텍처에서는 반드시 삼가야 한다고 함), 데이터베이스 간의 중복 데이터를 방지할 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-13 오후 9.24.38.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NDhOJ/btstX91mTSH/7RLhIUxmMylUG4q1IWTKr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NDhOJ/btstX91mTSH/7RLhIUxmMylUG4q1IWTKr1/img.png&quot; data-alt=&quot;Service Based Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NDhOJ/btstX91mTSH/7RLhIUxmMylUG4q1IWTKr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNDhOJ%2FbtstX91mTSH%2F7RLhIUxmMylUG4q1IWTKr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;458&quot; data-filename=&quot;스크린샷 2023-09-13 오후 9.24.38.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Service Based Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;서비스 계약&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 기반 아키텍처에서는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;서비스 기반 계약&lt;/b&gt;&lt;/span&gt;과 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;소비자 중심 계약&lt;/b&gt;&lt;/span&gt;이라는 두 가지 기본 유형의 서비스 계약 모델을 사용할 수 있습니다. &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두&lt;/span&gt; 계약의 차이점은 협업의 정도&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  서비스 기반 계약&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 기반 계약의 경우 서비스는 계약의 유일한 소유자이며, 일반적으로 서비스 소비자의 요구를 고려하지 않고 계약을 자유롭게 발전시키고 변경할 수 있습니다. 이 계약은 서비스 소비자가 새로운 서비스 기능을 필요로 하거나 원하는지 여부에 관계없이 모든 서비스 소비자가 새로운 서비스 계약 변경 사항을 채택하도록 강제합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  소비자 중심 계약&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;소비자 중심 계약은 소비자가 협력하여 계약을 정의합니다. 그런 다음 서비스 공급자는 계약을 충족하기 위해 서비스를 구현합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 계약에서는 일반적으로 서비스 소비자가 누구인지, 각 서비스 소비자가 서비스를 사용하는 방법을 알아야 합니다. 서비스 소비자는 계약 변경을 자유롭게 제안할 수 있으며, 서비스는 다른 서비스 소비자에게 미치는 영향에 따라 이를 채택하거나 거부할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;서비스 계약 버전 관리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 계약의 맥락에서 또 다른 중요한 주제는 계약 버전 관리입니다. 계약 버전 관리를 사용하면 계약 변경을 포함하는 새로운 서비스 기능을 출시하는 동시에 이전 계약을 아직 사용하고 있는 서비스 소비자에게 이전 버전과의 호환성을 제공할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 그림에서 소비자 A와 소비자 B가 사용하는 계약은 동일하지만 버전 번호가 다릅니다. 예를 들어 버전 1.0에서는 의사소통 방법이 XML 기반일 수 있지만 버전 1.1에서는 JSON 기반에 요청사항 필드가 추가된 버전입니다. 이때 버전 1.0은 호환성을 유지하기 위해 바로 없앨 수 없습니다. 이는 위에서 설명한 소비자 중심 계약에 대한 개념에 더 가깝습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-13 오후 10.54.26.png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rgKaJ/btstY5EpoDU/MqyLNkDUGcRQBdg8iUA3n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rgKaJ/btstY5EpoDU/MqyLNkDUGcRQBdg8iUA3n0/img.png&quot; data-alt=&quot;계약 버전 번호&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rgKaJ/btstY5EpoDU/MqyLNkDUGcRQBdg8iUA3n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrgKaJ%2FbtstY5EpoDU%2FMqyLNkDUGcRQBdg8iUA3n0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1015&quot; height=&quot;159&quot; data-filename=&quot;스크린샷 2023-09-13 오후 10.54.26.png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;계약 버전 번호&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;데이터베이스 분할&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스 기반 아키텍처의 여러 서비스는 중앙 공유 데이터베이스를 사용할 수 있습니다. 그러나 이러한 데이터베이스 커플링은 테이블 스키마 변경 시 문제가 발생할 수 있습니다. 예를 들어 테이블 스키마를 올바르게 변경하지 않은 경우 해당 변경으로 인해 모든 서비스에 영향을 미치기 때문에 이러한 변경 작업은 많은 노력이 필요하게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  단일 공유 라이브러리 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 서비스의 엔티티 객체가 공유하는 단일 라이브러리를 생성하는 것은 서비스 기반 아키텍처 관점에서 가장 비효율적인 구현 방법입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;테이블 구조가 조금이라도 변경이 발생하면 해당 엔티티 객체가 포함된 단일 공유 라이브러리도 같이 변경을 해야 하는데, 변경된 테이블의 사용 여부와 상관없이 전체 서비스를 일제히 변경 후 재배포할 수밖에 없습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-13 오후 11.06.19.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1HWnv/btstTsHtw3z/H1c99n79vCEGrArkFbGN81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1HWnv/btstTsHtw3z/H1c99n79vCEGrArkFbGN81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1HWnv/btstTsHtw3z/H1c99n79vCEGrArkFbGN81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1HWnv%2FbtstTsHtw3z%2FH1c99n79vCEGrArkFbGN81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;531&quot; data-filename=&quot;스크린샷 2023-09-13 오후 11.06.19.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  여러 공유 라이브러리 사용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터베이스 변경 영향도와 리스크를 낮추는 방법은 데이터베이스를 논리적으로 분할하고, 이러한 분할을 연합 공유 라이브러리를 통해 명시하는 것입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;데이터베이스를 논리적으로 분할함으로써 변경의 여파를 줄일 수 있습니다. 또한 모든 서비스의 공통 테이블을 변경하기 전에 먼저 해당 데이터베이스에 접근하고 있는 서비스들과 사전에 조율하는 과정이 필요합니다. 그리고 수정 권한을 관리자에게 맡김으로써 아무나 변경할 수 없도록 해야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-13 오후 11.18.15.png&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8rsqp/btstXzswepU/OJHCJKD4kYx3IVpY3Q2GK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8rsqp/btstXzswepU/OJHCJKD4kYx3IVpY3Q2GK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8rsqp/btstXzswepU/OJHCJKD4kYx3IVpY3Q2GK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8rsqp%2FbtstXzswepU%2FOJHCJKD4kYx3IVpY3Q2GK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1004&quot; height=&quot;634&quot; data-filename=&quot;스크린샷 2023-09-13 오후 11.18.15.png&quot; data-origin-width=&quot;1004&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Service Based Architecture의 장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;분산 아키텍처에 속하긴 하나 서비스 기반 아키텍처의 도메인 서비스는 큼지막한 단위로 구성되므로 다른 분산 아키텍처에 비해 ACID 트랜잭션이 더 잘 보존할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;분산 아키텍처 중에서 가장 구현하기 쉽고, 비용면에서 효율적입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;span style=&quot;position: absolute; width: 0px; height: 0px; font-size: 0px;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스가 여러개이고, 도메인 서비스 간 상호 통신을 방지해야 하는데 실제 운영 상황에서는 이를 구현하기 어렵지 않을까? 생각이 듭니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oreilly.com/library/view/microservices-vs-service-oriented/9781491975657/ch01.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.oreilly.com/library/view/microservices-vs-service-oriented/9781491975657/ch01.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/news/2016/10/service-based-architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.infoq.com/news/2016/10/service-based-architecture/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.architecturemaker.com/what-is-service-based-architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.architecturemaker.com/what-is-service-based-architecture/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>service based architecture</category>
      <category>서비스 기반 아키텍처</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/490</guid>
      <comments>https://kdg-is.tistory.com/entry/Service-Based-Architecture#entry490comment</comments>
      <pubDate>Wed, 13 Sep 2023 23:40:55 +0900</pubDate>
    </item>
    <item>
      <title>Microkernel Architecture</title>
      <link>https://kdg-is.tistory.com/entry/MicroKernel-Architecture</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;growtika-wLknZfsKmxQ-unsplash.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqnQXE/btstyux4gxy/rypLLrNaqbgbF2f4u78rHk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqnQXE/btstyux4gxy/rypLLrNaqbgbF2f4u78rHk/img.jpg&quot; data-alt=&quot;By unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqnQXE/btstyux4gxy/rypLLrNaqbgbF2f4u78rHk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqnQXE%2Fbtstyux4gxy%2FrypLLrNaqbgbF2f4u78rHk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-filename=&quot;growtika-wLknZfsKmxQ-unsplash.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;By unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Microkernel Architecture란&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;마이크로커널 아키텍처는 플러그인 아키텍처라고도 합니다. 이 아키텍처는 코어 시스템과 플러그인 컴포넌트라는 두 가지 아키텍처 요소로 구성된 단순한 모놀리식 아키텍처입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;간단한 예시를 들면 Spring으로 개발을 해봤다면 Eclipse, InteliJ 같은 IDEA를 많이 사용해보고 Spring Plugin도 많이 접해보셨을거라 생각합니다. 여기서 IDEA는 Core System에 속하고 번역 플러그인 같은것들은 Plugin Component에 속하게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-09 오후 1.27.58.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N4j68/btstyux2Rxw/5nLyK3aHbtF8YiGqwE1D2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N4j68/btstyux2Rxw/5nLyK3aHbtF8YiGqwE1D2k/img.png&quot; data-alt=&quot;기본적인 Microkernel Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N4j68/btstyux2Rxw/5nLyK3aHbtF8YiGqwE1D2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN4j68%2Fbtstyux2Rxw%2F5nLyK3aHbtF8YiGqwE1D2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1894&quot; height=&quot;500&quot; data-filename=&quot;스크린샷 2023-09-09 오후 1.27.58.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기본적인 Microkernel Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Core System&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코어 시스템은 시스템을 실행시키는데 필요한 최소한의 기능을 제공합니다. 위 예시에서 IDEA는 파일을 열고, 수정하고, 저장하는 기능을 제공합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코어 시스템은 다양한 플러그인을 등록하고, 라이프 사이클을 관리하며 플러그인 간 통신, 동적 플러그인 교체와 같은 중요한 기능을 제공합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코어 시스템은 규모와 복잡도에 따라 레이어드 아키텍처나 모듈러 모놀리스로 구현할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-09 오후 1.50.15.png&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mepGa/btstwf2Xcmk/lEU26RE5ow4SffIjKgKT21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mepGa/btstwf2Xcmk/lEU26RE5ow4SffIjKgKT21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mepGa/btstwf2Xcmk/lEU26RE5ow4SffIjKgKT21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmepGa%2Fbtstwf2Xcmk%2FlEU26RE5ow4SffIjKgKT21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2072&quot; height=&quot;564&quot; data-filename=&quot;스크린샷 2023-09-09 오후 1.50.15.png&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Plugin Component&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트는 특수한 처리 로직, 부가 기능, 코어 시스템을 개선/확장하기 위한 커스텀 코드가 구현된 컴포넌트입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;변동성이 매우 큰 코드를 분리하여 애플리케이션 내부의 유지보수성과 시험성을 높이고, 플러그인들은 상호독립적으며 의존성이 없습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트를 분리함으로써&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; 관심사를 분리&lt;/b&gt;&lt;/span&gt;할 수 있으며, 각 플러그인은 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;단일 책임 원칙&lt;/b&gt;&lt;/span&gt;을 준수할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트와 코어 시스템은 보통 point-to-point 통신을 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;컴파일 기반 플러그인 컴포넌트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;컴파일 기반의 플러그인 컴포넌트는 관리하기 편하지만, 변경, 추가, 삭제 시 전체 모놀리식 애플리케이션을 재배포해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;런타임 기반 플러그인 컴포넌트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;런타임 기반의 플러그인 컴포넌트는 재배포하지 않고, 추가, 삭제가 가능합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Core System과 Plugin Component의 소통 방법&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트가 반드시 코어 시스템과 point-to-point 방법으로 통신을 할 필요는 없습니다. 각 플러그인 컴포넌트는 REST나 메시징 등 다른 방법으로 호출하는 방법도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트 간에 직접 호출할 수 없으며 메시지는 코어 시스템에 전달되어야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-09 오후 2.20.34.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7000l/btsts3hrVE7/j15Q38GU8kpVJ4kjagwAj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7000l/btsts3hrVE7/j15Q38GU8kpVJ4kjagwAj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7000l/btsts3hrVE7/j15Q38GU8kpVJ4kjagwAj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7000l%2Fbtsts3hrVE7%2Fj15Q38GU8kpVJ4kjagwAj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1828&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2023-09-09 오후 2.20.34.png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;레지스트리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;코어 시스템은 어떤 플러그인을 사용할 수 있는지, 해당 플러그인을 가져오려면 어떻게 해야 하는지 알고 있어야 합니다. 가장 일반적인 방법은 플러그인 레지스트리를 경유하는 방법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;이 레지스트리에는 플러그인의 명칭, 데이터 계약, 세부 원격 엑세스 프로토콜 등 각 플러그인 모듈에 관한 정보가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Microkernel Architecture의 장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코어 시스템의 변경하지 않고도 새로운 기능을 추가하거나 수정하는 것이 더 쉬워집니다. 따라서 확장성과 유연성을 향상시킬 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개별 플러그인 컴포넌트별로 테스트할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;플러그인 컴포넌트끼리 통신을 할 수 없고 코어 시스템을 거쳐 통신을 해야하므로 오버헤드가 증가할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://priyalwalpita.medium.com/software-architecture-patterns-microkernel-architecture-97cee200264e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://priyalwalpita.medium.com/software-architecture-patterns-microkernel-architecture-97cee200264e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.alibabacloud.com/blog/what-is-microkernel-architecture-design_597605&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.alibabacloud.com/blog/what-is-microkernel-architecture-design_597605&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/understanding-microkernel-architecture-foundation-modular-nasr-ullah&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.linkedin.com/pulse/understanding-microkernel-architecture-foundation-modular-nasr-ullah&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>microkernel architecture</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/489</guid>
      <comments>https://kdg-is.tistory.com/entry/MicroKernel-Architecture#entry489comment</comments>
      <pubDate>Sat, 9 Sep 2023 14:51:33 +0900</pubDate>
    </item>
    <item>
      <title>PipeLine Architecture</title>
      <link>https://kdg-is.tistory.com/entry/PipeLine-Architecture</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;elimende-inagella-zx7VUt9txos-unsplash.jpg&quot; data-origin-width=&quot;6621&quot; data-origin-height=&quot;4419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8gD1J/btstwZFyfda/HPeBh65FryIpCuNKJyRC8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8gD1J/btstwZFyfda/HPeBh65FryIpCuNKJyRC8K/img.jpg&quot; data-alt=&quot;By unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8gD1J/btstwZFyfda/HPeBh65FryIpCuNKJyRC8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8gD1J%2FbtstwZFyfda%2FHPeBh65FryIpCuNKJyRC8K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;6621&quot; height=&quot;4419&quot; data-filename=&quot;elimende-inagella-zx7VUt9txos-unsplash.jpg&quot; data-origin-width=&quot;6621&quot; data-origin-height=&quot;4419&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;By unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;PipeLine&amp;nbsp;Architecture란&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;PipeLine Architecture는 시스템의 작업을 몇 개의 순차적인 프로세싱 단계들로 구분할 수 있고, 이 단계들은 파이프를 통해 연결됩니다. 그렇기 때문에 어떤 단계의 출력 데이터는 다음 단계의 입력 데이터가됩니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;초기에는 데이터를 제공해줄 수 있는 Data Source가 있어야 합니다. 그런 다음 단계적으로 데이터는 파이프를 통해 필터로 전달됩니다. 그리고 각 필터는 전달받은 데이터를 변환하고 변환된 데이터를 파이프를 통해 다음 필터로 전달하고 최종적으로 Consumer에게 도착하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-07 오전 10.14.34.png&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Wgzy/btstnwoUXRo/xUKfaOpHhaYUiPCK70Yhqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Wgzy/btstnwoUXRo/xUKfaOpHhaYUiPCK70Yhqk/img.png&quot; data-alt=&quot;PipeLine Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Wgzy/btstnwoUXRo/xUKfaOpHhaYUiPCK70Yhqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Wgzy%2FbtstnwoUXRo%2FxUKfaOpHhaYUiPCK70Yhqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1690&quot; height=&quot;362&quot; data-filename=&quot;스크린샷 2023-09-07 오전 10.14.34.png&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PipeLine Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Pipe&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;파이프는 필터에서 다음 필터로 데이터를 전달하는 연결자입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;파이프는 성능상의 이유로 보통 단방향, 점대점 방식으로 구성됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;파이프를 오가는 페이로드의 데이터는 어떤 포맷도 가능하지만, 아키텍트는 고성능에 유리한 적은 양의 데이터를 선호합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;일반적으로 다음 필터가 파이프가 가지고있는 데이터를 처리할 시간이 있을 때까지 모든 데이터를 저장하기 위해 파이프는 데이터 버퍼에 의해 구현됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Filter&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;필터는 다른 서비스나 모듈 등을 호출하지 않아도 동작하고, 다른 필터와 독립적이며, 일반적으로 무상태성입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;필터는 한가지 작업만 수행하므로 복합 태스크는 여러 필터를 이어 붙여 처리하면 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;필터는 출력을 하기 위해 들어온 모든 데이터를 한꺼번에 처리하는것이 아닌 입력이 일정량 들어오는 대로 출력을 바로바로 하는 방식으로 데이터를 처리합니다. 이렇게 해야 낮은 레이턴시를 만들 수 있고, 병렬 프로세싱이 가능하게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  Filter의 종류&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;프로듀서(producer)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로세스의 시작점이고, 아웃바인드만 있어서 source라고도 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;변환기(transformer)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;입력을 받아 필요시 일부 또는 전체 데이터를 변환한 후, 그 결과를 아웃바운드 파이프로 전달합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;함수형 프로그래밍의 열혈팬들은 이 기능을 map이라고 부릅니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;테스터(tester)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;입력을 받아 하나 이상의 기준에 대해 테스트하고, 그 결과에 따라 필요시 결과를 생산합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;함수형 프로그래머는 이 기능을 reduce라고 부릅니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;컨슈머(consumer)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;파이프라인 흐름의 종착역입니다. 컨슈머는 파이프라인 프로세스의 최종 결과를 데이터베이스에 저장하거나 화면에 표시하는 역할을 수행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;PipeLine Architecture의 장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로세싱의 순서를 유연하게 변경할 수 있고, 재조합할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특정 프로세스를 제외하거나 새로 추가할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;느슨한 결합으로 인해 독립적인 개발을 진행할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하나의 필터를 수정하더라도 다른 필터에 영향을 미치지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;필터는 재사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 14px; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;전역 상태정보를 공유하게 된다면 비용이 많이들고 유연성을 저하시키는 원인이 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  전역 상태정보를 공유하고자할 때 어떤 방법이 있을까?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래와 같은 방법을 사용할 수 있다고 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 단계들이 Symbol table을 사용하여 전역 정보를 공유합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 이 방식은 필터는 이전 필터가 보내준 데이터에 대해서만 얻는다는 규칙을 위반하게 됩니다. 왜 이 규칙을 위반하면서까지 해당 방법을 사용하는 걸까요? 그 이유는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;성능 때문&lt;/b&gt;&lt;/span&gt;이라 합니다. 파이프가 전역 상태정보를 추가 데이터로 넘겨준다면 데이터 구조가 복잡해질 수도 있고, 파이프의 데이터 크기가 커집니다. 이렇게 전역 상태 정보를 따로 관리함으로써 파이프가 최소한의 데이터만 넘길 수 있도록 설계한 것입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-07 오전 10.06.24.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Wnqb/btstkv5s9tT/m4G3qnsfTkJF56mxvhQOEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Wnqb/btstkv5s9tT/m4G3qnsfTkJF56mxvhQOEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Wnqb/btstkv5s9tT/m4G3qnsfTkJF56mxvhQOEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Wnqb%2Fbtstkv5s9tT%2Fm4G3qnsfTkJF56mxvhQOEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1372&quot; height=&quot;722&quot; data-filename=&quot;스크린샷 2023-09-07 오전 10.06.24.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.dossier-andreas.net/software_architecture/pipe_and_filter.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.dossier-andreas.net/software_architecture/pipe_and_filter.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://johngrib.github.io/wiki/pattern/pipeline/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://johngrib.github.io/wiki/pattern/pipeline/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/software-architectural-patterns-pipes-filters-lakmal-kankanamge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.linkedin.com/pulse/software-architectural-patterns-pipes-filters-lakmal-kankanamge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.slmanju.com/2020/05/pipes-and-filters-architecture.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.slmanju.com/2020/05/pipes-and-filters-architecture.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>pipe and filter architecture</category>
      <category>pipeline architecture</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/488</guid>
      <comments>https://kdg-is.tistory.com/entry/PipeLine-Architecture#entry488comment</comments>
      <pubDate>Thu, 7 Sep 2023 10:22:44 +0900</pubDate>
    </item>
    <item>
      <title>Layered Architecture</title>
      <link>https://kdg-is.tistory.com/entry/Layered-Architecture</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;freestocks-RgKmrxpIraY-unsplash.jpg&quot; data-origin-width=&quot;5184&quot; data-origin-height=&quot;3456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OLAJC/btstayVPQTS/jHQjKqfGwAxGQ2CyoB3yf0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OLAJC/btstayVPQTS/jHQjKqfGwAxGQ2CyoB3yf0/img.jpg&quot; data-alt=&quot;By unsplash&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OLAJC/btstayVPQTS/jHQjKqfGwAxGQ2CyoB3yf0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOLAJC%2FbtstayVPQTS%2FjHQjKqfGwAxGQ2CyoB3yf0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;5184&quot; height=&quot;3456&quot; data-filename=&quot;freestocks-RgKmrxpIraY-unsplash.jpg&quot; data-origin-width=&quot;5184&quot; data-origin-height=&quot;3456&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;By unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Layered Architecture란&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;Layered Architecture란 가장 흔한 아키텍처 스타일 중 하나이고, n 티어 아키텍처라 부르기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;관심사의 분리를 통해 각 레이어는 자신의 역할에 맞는 책임만을 수행&lt;/b&gt;&lt;/span&gt;합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-06 오후 8.34.37.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o7WRP/btstmuktVMN/xYCdunc3k7lBotkiwOhTn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o7WRP/btstmuktVMN/xYCdunc3k7lBotkiwOhTn1/img.png&quot; data-alt=&quot;Layerd Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o7WRP/btstmuktVMN/xYCdunc3k7lBotkiwOhTn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo7WRP%2FbtstmuktVMN%2FxYCdunc3k7lBotkiwOhTn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;495&quot; data-filename=&quot;스크린샷 2023-09-06 오후 8.34.37.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Layerd Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Presentation Layer&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 영역은 사용자로부터 입력받은 값들에 대해 유효성 검증을 하고, Business Layer에게 요청을 위임하는 동시에 사용자에게 응답을 반환하는 역할을 수행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Business Layer&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 영역은 Presentation Layer로부터 전달받은 값을 사용하여 알맞는 요구사항을 만족하는 역할을 수행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Persistence Layer&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;도메인 엔티티와 매핑하는 역할을 수행합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Infrastructure Layer&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 영역은 Database Layer를 조금 더 포괄적으로 표현하고자 Infrastructure Layer라는 단어를 사용하여 표현하겠습니다. 이 영역은 도메인 엔티티를 데이터베이스에 저장할 수도 있고, Rest API를 사용하여 다른 서비스를 호출할 수도 있고, 메시징 큐를 사용할 수도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;레이어 격리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-05 오후 10.35.21.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyqytN/btss3XbjuZ3/aKHsxN5D0e0ZyfIC8u2ldk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyqytN/btss3XbjuZ3/aKHsxN5D0e0ZyfIC8u2ldk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyqytN/btss3XbjuZ3/aKHsxN5D0e0ZyfIC8u2ldk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyqytN%2Fbtss3XbjuZ3%2FaKHsxN5D0e0ZyfIC8u2ldk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;483&quot; data-filename=&quot;스크린샷 2023-09-05 오후 10.35.21.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;위 그림은 레이어드 아키텍처의 흐름도입니다. 여기서의 각 레이어는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;폐쇄&lt;/b&gt;&lt;/span&gt;&amp;nbsp;또는&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개방&lt;/span&gt;&amp;nbsp;&lt;/b&gt;상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;폐쇄 레이어&lt;/b&gt;&lt;/span&gt;란 상위 레이어에서 하위 레이어로 이동하므로 중간의 어떤 레이어도 건너뛸 수 없고, 다음 레이어를 거쳐야 비로소 그 다음의 레이어에 진입할 수 있습니다.&amp;nbsp;이를 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;fast-lane reader pattern&lt;/b&gt;&lt;/span&gt;(추월 차선 리더 패턴)이라 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;레이어 격리는 어느 레이어에서 변경이 발생하더라도 다른 레이어에 있는 컴포넌트에 아무런 영향을 미치지 않는 것입니다. 이렇게 레이어 격리가 잘 지켜지기 위해서는 각 레이어간 격리성을 잘 준수할 수 있도록 노력을 기울여야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;  &lt;b&gt;fast-lane reader pattern에 대한 개인적인 의견&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;읽기 전용 API에 한정&lt;/b&gt;&lt;/span&gt;하여 Presentation Layer에서 Persistence Layer로 직접적인 접근은 괜찮다고 생각합니다. 그리고 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Persistence Layer에서 반환하는 객체가 도메인 엔티티가 아닌 DTO 객체라는 범위 내에서만 한정&lt;/b&gt;&lt;/span&gt;됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;레이어 개방&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위에서는 레이어 격리에 대해 알아보았습니다. 레이어 격리는 이해하기 쉽고, 아 그렇구나! 할 수 있는 부분이 많았습니다. 하지만 레이어 개방에 대해서는 다소 이해하기 어려운 부분이 있습니다. 그렇기 때문에 하나의 상황을 가정하고 살펴보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;예를 들어 어떠한 개발자는 사용자의 요청에 의해 상품의 수량이 감소하는 로직을 구현해야 하는 상황입니다. 그렇기 때문에 개발자는 해당 로직에 접근하기 전에 임계영역을 설정하고 싶어합니다. 이러한 상황에서 레이어를 하나 추가함으로써 해당 요청건만 추가적인 레이어에 접근하고 기존에 있던 다른 요청들은 해당 레이어에 접근을 하지 않도록 하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아래 그림에서는 수량이 감소하는 요청이 있을 경우 Facade Layer에서 Lock을 획득하고 Business Layer로 넘어가는 것입니다. 그렇지 않은 경우에는 굳이 Facade Layer를 지나칠 필요가 없기 때문에 Business Layer에 접근하는 것입니다. 이때 Facade Layer는 상황에 따라 지나칠 수 있어야 하므로 항상 공개된 상태를 가지는 Layer가 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.57.03.png&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdagt2/btstfdkbFKS/U8CjPZXjTJaiZZ3h44Pz9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdagt2/btstfdkbFKS/U8CjPZXjTJaiZZ3h44Pz9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdagt2/btstfdkbFKS/U8CjPZXjTJaiZZ3h44Pz9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdagt2%2FbtstfdkbFKS%2FU8CjPZXjTJaiZZ3h44Pz9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;596&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.57.03.png&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;Infrastructure Layer에 직접 접근하는것을 최소한으로 하자&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;만약 사용자가 파일 업로드를 한다고 가정을 해봅시다.&amp;nbsp;이때 사용자는 AWS 버킷에 파일을 업로드 하거나 NCP, GCP의 버킷에 업로드를 할 수 있습니다. 아래 그림은 사용자가 파일을 업로드 하는 과정을 간단히 그린 예시입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;말하고자 하는 것은 Business Layer에서 Infrastructure Layer에 접근할 때 Infrastructure Layer에 구현되어 있는 구체 클래스에 직접적으로 접근을 하는 경우입니다. 만약 이렇게 된다면 Infrastructure Layer에 구현된 구체 클래스에서 변경이 발생했을 경우 영향이 미치는 범위는 Business Layer까지입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.19.48.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PhjIL/btstfmVCoQ9/t6HDGFQk7NwLk4v6FkQcn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PhjIL/btstfmVCoQ9/t6HDGFQk7NwLk4v6FkQcn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PhjIL/btstfmVCoQ9/t6HDGFQk7NwLk4v6FkQcn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPhjIL%2FbtstfmVCoQ9%2Ft6HDGFQk7NwLk4v6FkQcn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;98&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.19.48.png&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;어떻게 해결할 수 있을까?&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;파일을 업로드 하는 행위는 어디에 업로드할지 Business Layer가 알 필요가 없습니다. 또한 상황에 따라 AWS, NCP, GCP 다양한 버킷에 업로드가 될 수 있기 때문입니다. 이때 인터페이스를 활용하여 해당 문제를 해결할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.33.04.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQS0Lc/btstmudJEFi/W7st5HocSW0R9CFBtNyfd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQS0Lc/btstmudJEFi/W7st5HocSW0R9CFBtNyfd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQS0Lc/btstmudJEFi/W7st5HocSW0R9CFBtNyfd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQS0Lc%2FbtstmudJEFi%2FW7st5HocSW0R9CFBtNyfd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;399&quot; data-filename=&quot;스크린샷 2023-09-06 오후 9.33.04.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Layered Architecture의&amp;nbsp;&lt;/b&gt;&lt;/span&gt;주의사항&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;  아키텍처 싱크홀 안티패턴&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 안티패턴은 사용자의 요청이 각 레이어에서 다른 레이어로 이동할 때 각 레이어에서 아무런 비지니스 로직도 처리하지 않고 그냥 지나쳐가는 안티패턴입니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 안티패턴의 단점으로는 쓸때없는 자원을 소모합니다. Spring을 사용한다면 사용자의 요청이 왔을 때 하나의 Thread가 해당 요청을 수행하게 되는데 Thread의 CallStack에 의미가 없는 stack이 하나 더 추가됩니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-05 오후 11.50.54.png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbREOU/btsteN53sFg/ZqXCJ8WhNQLGCLq4kB1ml1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbREOU/btsteN53sFg/ZqXCJ8WhNQLGCLq4kB1ml1/img.png&quot; data-alt=&quot;아키텍처 싱크홀 안티패턴의 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbREOU/btsteN53sFg/ZqXCJ8WhNQLGCLq4kB1ml1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbREOU%2FbtsteN53sFg%2FZqXCJ8WhNQLGCLq4kB1ml1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;86&quot; data-filename=&quot;스크린샷 2023-09-05 오후 11.50.54.png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아키텍처 싱크홀 안티패턴의 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해결방안&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아키텍처 싱크홀 안티패턴으로 처리 중인 요청의 전체 비율을 파악하는 것이 중요합니다. 80 대 20 법칙을 적용해서 전체 요청의 20%가 싱크홀인 정도면 괜찮은 수준입니다. 그러나 반대로 80%가 싱크홀이라면 문제가 있는 상황입니다. 이때는 레이어드 아키텍처가 적합하지 않다는 증거입니다. 즉 다른 아키텍처를 고민해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;모든 레이어를 개방합니다. 그러나 개방했다는 것은 응집도를 낮아지고 결합도가 높아지므로 트레이드오프를 해야합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개인적인 생각으로 아키텍처 싱크홀 안티패턴은 읽기 API가 요청한 작업을 단순 serving하므로 fast-lane reader pattern을 적용하여 해결할 수 있지 않을까? 생각합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Layered Architecture의&amp;nbsp;&lt;/b&gt;&lt;/span&gt;장단점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;font-size: 14px; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;도메인이 복잡하지 않은 경우 적절합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;각 레이어를 통해 일관성을 지킬 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;font-size: 14px; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로젝트의 규모가 커질수록 관리하기 어렵고 확장성이 떨어집니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;레이어로 분리된 관심사를 제외한 다른 관심사가 발견되는 경우 패키지 분리나 적절한 코드 배치가 어렵습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;참고&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://improvedesignpatterns.blogspot.com/2010/06/fast-line-reader-or-fast-lane-reader.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://improvedesignpatterns.blogspot.com/2010/06/fast-line-reader-or-fast-lane-reader.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://msolo021015.medium.com/layered-architecture-deep-dive-c0a5f5a9aa37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://msolo021015.medium.com/layered-architecture-deep-dive-c0a5f5a9aa37&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://priyalwalpita.medium.com/software-architecture-patterns-layered-architecture-a3b89b71a057&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://priyalwalpita.medium.com/software-architecture-patterns-layered-architecture-a3b89b71a057&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/layered-architecture-is-good&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dzone.com/articles/layered-architecture-is-good&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/riiid-teamblog-kr/gradle%EA%B3%BC-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-backend-layered-architecture-97117b344ba8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/riiid-teamblog-kr/gradle%EA%B3%BC-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-backend-layered-architecture-97117b344ba8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>layered architecture</category>
      <category>레이어드 아키텍처</category>
      <category>레이어드 아키텍처란</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/487</guid>
      <comments>https://kdg-is.tistory.com/entry/Layered-Architecture#entry487comment</comments>
      <pubDate>Wed, 6 Sep 2023 22:03:15 +0900</pubDate>
    </item>
    <item>
      <title>JDK Dynaimc Proxy와 CGLIB에 대해</title>
      <link>https://kdg-is.tistory.com/entry/JDK-Dynaimc-Proxy%EC%99%80-CGLIB%EC%97%90-%EB%8C%80%ED%95%B4</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring을 사용하여 개발을 하다보면 @Transactional이라는 어노테이션을 많이 보고, 많이 사용하곤 합니다. 이때 클래스 레벨이나 메서드 레벨에 @Transactional 어노테이션이 선언되어 있으면 Proxy로 수행이 되는구나 생각만하고 지나치는 경우가 있습니다. 하지만 이러한 Proxy로 인해 AOP Self Invocation과 같은 겪고 싶지 않은 여러 경험들을 하게 될 수 있습니다. 그렇기 때문에 우리는 스프링에서 Proxy가 어떻게 동작하는지 알아야할 필요성이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;JDK Dynamic Proxy&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK Dynamic Proxy는 java.lang.reflect 패키지에 속한 Proxy 클래스를 사용합니다.&amp;nbsp; 따라서 리플랙션을 사용하여 동적으로 프록시를 생성해주기 때문에 이름부터 Dynamic Proxy입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  JDK Dynamic Proxy를 사용하여 프록시를 생성하는 방법&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래는 JDK Dynamic Proxy를 사용하여 프록시 객체를 생성하는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693631787131&quot; class=&quot;delphi&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Object proxy = Proxy.newProxyInstance(ClassLoader       // 클래스로더
                                    , Class&amp;lt;?&amp;gt;[]        // 타깃의 인터페이스
                                    , InvocationHandler // 타깃의 정보가 포함된 Handler
               );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  코드로 살펴보자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 코드를 보면 하나의 인터페이스를 만들고 두개의 구현체가 있으며, 프록시 핸들러를 만들었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693633417085&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 인터페이스를 만듭니다.
public interface PaymentService {

    void pay();
}

// 2. 인터페이스를 구현한 구체 클래스를 만듭니다.
public class KakaoPayService implements PaymentService {

    @Override
    public void pay() {
        System.out.println(&quot;카카오페이를 사용하여 결제...&quot;);
    }
}

public class NaverPayService implements PaymentService {

    @Override
    public void pay() {
        System.out.println(&quot;네이버페이를 사용하여 결제...&quot;);
    }
}

// 3. 프록시 핸들러를 구현합니다.
public class PaymentInvocationHandler implements InvocationHandler {

    private Object target;

    public PaymentInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;▶︎ 실행&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행을 시켜보면 의도한대로 결과가 도출되는 것을 알 수 있습니다. 또한 클래스의 이름을 출력해보면 com.sun.proxy.&amp;amp;Proxy0 이라는 것을 알 수 있으며 이를 보고 인터페이스를 사용하여 프록시를 생성했구나 알 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693633503932&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {

    public static void main(String[] args) {
        PaymentService kakaoPayService = (PaymentService) Proxy.newProxyInstance(PaymentService.class.getClassLoader(),
                new Class[]{PaymentService.class},
                new PaymentInvocationHandler(new KakaoPayService())
        );

        PaymentService naverPayService = (PaymentService) Proxy.newProxyInstance(PaymentService.class.getClassLoader(),
                new Class[]{PaymentService.class},
                new PaymentInvocationHandler(new NaverPayService())
        );

        System.out.println(&quot;naverPayService class : &quot; + naverPayService.getClass().getName()); // naverPayService class : com.sun.proxy.$Proxy0
        System.out.println(&quot;kakaoPayService class : &quot; + kakaoPayService.getClass().getName()); // kakaoPayService class : com.sun.proxy.$Proxy0
        naverPayService.pay(); // 네이버페이를 사용하여 결제...
        kakaoPayService.pay(); // 카카오페이를 사용하여 결제...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;동작 원리&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 메서드를 수행하면 JDK Dynamic Proxy가 메서드 처리를 Invocateion Handler에게 위임한 후 Invocation Handler는 부가 기능을 수행한 뒤에 Target에게 기능을 위임합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-02 오후 3.15.12.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tLSX2/btssTm2cJE1/G4M69W5BQNh3TxvekuiKNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tLSX2/btssTm2cJE1/G4M69W5BQNh3TxvekuiKNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tLSX2/btssTm2cJE1/G4M69W5BQNh3TxvekuiKNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtLSX2%2FbtssTm2cJE1%2FG4M69W5BQNh3TxvekuiKNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1746&quot; height=&quot;256&quot; data-filename=&quot;스크린샷 2023-09-02 오후 3.15.12.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  결론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JDK Dynamic Proxy는 인터페이스 기반이다 보니 인터페이스가 필수 있습니다.&lt;/li&gt;
&lt;li&gt;리플랙션을 사용하다 보니 느립니다.&lt;/li&gt;
&lt;li&gt;InvocationHandler의 invoke 메서드를 구현해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;CGLIB&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGLIB는 Code Generator Library의 약자로 클래스의 바이트코드를 조작하여 Proxy 객체를 생성해주는 라이브러리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  CGLIB를 사용하여 프록시를 생성하는 방법&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래는 CGLIB를 사용하여 프록시 객체를 생성하는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693636551994&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(NaverPayService.class); // 타깃 클래스
         enhancer.setCallback(PaymentInterceptor);      // Handler
Object proxy = enhancer.create(); // Proxy 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  코드로 살펴보자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JDK Dynamic Proxy를 사용하면 인터페이스를 만든 후 해당 인터페이스를 구현한 구체 클래스를 만들어야하지만 CGLIB를 사용한 경우는 구체 클래스를 상속받아 만들어지므로 인터페이스를 만들 필요가 없습니다.&lt;/li&gt;
&lt;li&gt;MethodInterceptor 인터페이스를 구현한 구체 클래스에서는 Method 클래스의 invoke 메서드를 사용할 수도 있지만 MethodProxy 클래스의 invoke 메서드를 사용하는게 조금 더 빠르다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693637106935&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 클래스를 만듭니다.
public class KakaoPayService {

    public void pay() {
        System.out.println(&quot;카카오페이를 사용하여 결제...&quot;);
    }
}

public class NaverPayService {

    public void pay() {
        System.out.println(&quot;네이버페이를 사용하여 결제...&quot;);
    }
}

// 2. MethodInterceptor 인터페이스를 구현한 구체 클래스를 만듭니다.
public class PaymentInterceptor implements MethodInterceptor {

    private final Object target;

    public PaymentInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invoke(target, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;b&gt;▶︎&lt;/b&gt; 실행&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스의 이름을 출력해보면 CGLIB를 사용한 것을 알 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1693637315358&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {

    public static void main(String[] args) {
        NaverPayService target = new NaverPayService();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(NaverPayService.class);
        enhancer.setCallback(new PaymentInterceptor(target));

        NaverPayService naverPayService = (NaverPayService) enhancer.create();
        System.out.println(&quot;naverPayService class : &quot; + naverPayService.getClass().getName()); // naverPayService class : com.gift.kakao.domain.payment.NaverPayService$$EnhancerByCGLIB$$55c553e8
        naverPayService.pay(); //네이버페이를 사용하여 결제...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;동작 원리&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 메서드를 수행하면 CGLIB가 메서드 처리를 MethodInterceptor에게 위임한 후 MethodInterceptor는 부가 기능 수행한 뒤 Target에게 기능을 위임합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-09-02 오후 3.51.38.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2CuHv/btssNj6LayJ/B7jRZdRbKrxoSF5BLfC7i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2CuHv/btssNj6LayJ/B7jRZdRbKrxoSF5BLfC7i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2CuHv/btssNj6LayJ/B7jRZdRbKrxoSF5BLfC7i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2CuHv%2FbtssNj6LayJ%2FB7jRZdRbKrxoSF5BLfC7i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1610&quot; height=&quot;240&quot; data-filename=&quot;스크린샷 2023-09-02 오후 3.51.38.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  결론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리플랙션이 아닌 바이트 코드를 조작하므로 빠릅니다.&lt;/li&gt;
&lt;li&gt;인터페이스를 만들지 않아도 됩니다.&lt;/li&gt;
&lt;li&gt;상속을 이용하여 프록시 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;상속을 이용하기 때문에 final이 선언되어 있는 클래스는 프록시 객체를 생성할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  왜 처음부터 CGLIB을 사용하지 않았을까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK Dynamic Proxy는 리플랙션을 사용하고, 인터페이스가 있어야 합니다. 반면 CGLIB는 리플랙션을 사용하지도 않고, 인터페이스가 없어도 될 뿐만 아니라 바이트 코드를 조작하므로 속도도 빠릅니다. 하지만 왜 처음부터 CGLIB를 사용하지 않았을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1. Enhancer 라이브러리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CGLIB를 사용하기 위해서는 Enhancer 라이브러리가 필수입니다. Spring에서 이를 쓰기 위해서는 의존성을 추가했어야 했습니다.&lt;/li&gt;
&lt;li&gt;하지만 Spring 3.2 버전부터 spring-core로 리패키징이 된 상태입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2. default 생성자&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CGLIB를 구현하기 위해서는 반드시 default 생성자를 필요로 했습니다.&lt;/li&gt;
&lt;li&gt;하지만 Objenesis 라는 라이브러리를 사용하여 기본 생성자 없이도 객체 생성이 가능하게 되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3. 생성자 중복 호출&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 14px;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CGLIB Proxy의 메서드를 호출하면 타겟의 생성자가 2번 호출된다는 단점이 있었습니다.&lt;/li&gt;
&lt;li&gt;하지만 Objenesis 라이브러리를 사용하여 개선이 되었다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;현 상황에서 CGLIB는?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 4.3과 Spring Boot 1.4 버전부터는 기본적으로 CGLIB를 사용하고 있고 또한 JPA Hibernate에서도 기본적으로 CGLIB를 사용하고 있다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mangkyu.tistory.com/175&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title=&quot;https://www.youtube.com/watch?v=MFckVKrJLRQ&quot; href=&quot;https://www.youtube.com/watch?v=MFckVKrJLRQ&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/watch?v=MFckVKrJLRQ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA/Spring</category>
      <category>cglib</category>
      <category>JDK Dynamic Proxy</category>
      <category>JDK Dynamic Proxy와 CGLIB의 차이</category>
      <author>realizers</author>
      <guid isPermaLink="true">https://kdg-is.tistory.com/486</guid>
      <comments>https://kdg-is.tistory.com/entry/JDK-Dynaimc-Proxy%EC%99%80-CGLIB%EC%97%90-%EB%8C%80%ED%95%B4#entry486comment</comments>
      <pubDate>Sat, 2 Sep 2023 17:01:47 +0900</pubDate>
    </item>
  </channel>
</rss>