<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>매일 꾸준히, 더 깊이</title>
    <link>https://engineer-mole.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 11 May 2026 10:29:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발자 두더지</managingEditor>
    <image>
      <title>매일 꾸준히, 더 깊이</title>
      <url>https://tistory1.daumcdn.net/tistory/3863241/attach/d621edb1058e4226b5f41e4ee0407788</url>
      <link>https://engineer-mole.tistory.com</link>
    </image>
    <item>
      <title>Domain 이벤트 (도메인 이벤트)</title>
      <link>https://engineer-mole.tistory.com/459</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;시작하기에 앞서&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-line=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;도메인 이벤트는 도메인 중심 설계에서 사용되는 설계 패턴 중 하나로, 도메인 이벤트 자체는 단순한 개념이지만, 여러 문맥에 사용되기 때문에, 좀처럼 이해가 어려운 부분이 있다. 따라서 이번 기회를 통해 관련 내용을 정리하고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;2&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-line=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Domain 이벤트란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-line=&quot;29&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이벤트는 &quot;과거에 발생한 사건&quot;이며 도메인 이벤트는 &quot;비즈니스 도메인에서 발생한 중요한 사건을 나타내는 메시지&quot;이다(예: 주문이 할당되었으나 주문이 취소됨). 도메인 이벤트는 시스템의 상태 변경(=집약 상태의 변화)을 나타내며, 일반적으로 집약이 도메인 이벤트의 출처가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzbeH/dJMcahDRONq/aFbTt0d5IOKWKxKhWEN0e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzbeH/dJMcahDRONq/aFbTt0d5IOKWKxKhWEN0e1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzbeH/dJMcahDRONq/aFbTt0d5IOKWKxKhWEN0e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtzbeH%2FdJMcahDRONq%2FaFbTt0d5IOKWKxKhWEN0e1%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;1774&quot; height=&quot;471&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;471&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;용도&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans 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 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 이벤트 발생을 기점으로, 다른 처리에 대한 트리거가 된다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&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-origin-width=&quot;602&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DEHmA/dJMcaaksSnW/VMPSWStYHzrTg9pqF8ImP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DEHmA/dJMcaaksSnW/VMPSWStYHzrTg9pqF8ImP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DEHmA/dJMcaaksSnW/VMPSWStYHzrTg9pqF8ImP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDEHmA%2FdJMcaaksSnW%2FVMPSWStYHzrTg9pqF8ImP1%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;602&quot; height=&quot;284&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;52&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 요구사항으로 '...하고 싶은 경우...'라는 문구가 나오면 도메인 이벤트의 사용에 적합한 부분일지도 모른다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;54&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;54&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 집약간의 무결성을 가져옵니다(예: 주문이 취소된 경우 재고 반환)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;55&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 시스템과의 연계(예: 신규 예약이 들어오면 메일 송신, 외부 API 호출)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;56&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그 기록&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;57&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;etc.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;60&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;장점은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;62&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;62&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트의 발생원이 되는 처리와 관련되어 연동되는 처리를 분리할 수 있다(관심의 분리)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;63&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 코드를 변경하지 않고 이벤트에 반응하는 새로운 처리를 추가할 수 있다(확장성).&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;65&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 포스트에서는 이 내용을 중심으로 구현 방법을 설명하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;65&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;2.-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E8%A8%98%E9%8C%B2%E3%81%97%E6%B4%BB%E7%94%A8%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;67&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 도메인 이벤트 기록 및 활용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;69&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 지속하고 축적함으로써 현재 상태에 이르는 과정을 추적할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;71&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;71&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;감사 추적&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;72&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조사 및 디버깅&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;73&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans 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 id=&quot;3.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E6%83%85%E5%A0%B1%E6%BA%90%E3%81%A8%E3%81%97%E3%81%9F%E7%8A%B6%E6%85%8B%E7%AE%A1%E7%90%86%EF%BC%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%B3%E3%82%B0%EF%BC%89&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;75&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. 이벤트를 정보원으로 한 상태 관리(이벤트 소싱)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;77&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이벤트 소싱&lt;/b&gt;&amp;nbsp;은 시스템 상태를 이벤트의 연속으로 저장하는 설계 패턴이다. 도메인 개체의 상태를 직접 저장(=상태 소싱)하는 대신 발생한 모든 이벤트를 순차적으로 기록하고 해당 이벤트를 재생하여 현재 상태를 다시 작성한다. 이러한 이벤트 소싱은 종종 CQRS와 함께 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;77&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4.-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A8%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%AE%E8%B2%AC%E5%8B%99%E5%88%86%E9%9B%A2%EF%BC%88cqrs%EF%BC%89&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;81&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4. 명령 및 쿼리 책임 분리(CQRS)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;83&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;CQRS(Command Query Responsibility Segregation)&lt;/b&gt;&amp;nbsp;는 명령(쓰기 작업)과 쿼리(읽기 작업)를 분리하는 아키텍처 패턴이다. 집약(명령 모델)에 대한 변경사항을 이벤트로 알리고, 이를 수신한 이벤트 핸들러가 읽기 전용 뷰(쿼리 모델)를 구축하여 쓰기 및 읽기 각각에 최적화된 모델 및 아키텍처 특성을 가질 수 있게 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;83&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;5.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E4%B8%AD%E5%BF%83%E3%81%A8%E3%81%97%E3%81%9F%E3%83%93%E3%82%B8%E3%83%8D%E3%82%B9%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%81%AE%E5%88%86%E6%9E%90%E3%83%BB%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0%EF%BC%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%B9%E3%83%88%E3%83%BC%E3%83%9F%E3%83%B3%E3%82%B0%EF%BC%89&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;86&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;5. 이벤트를 중심으로 한 비즈니스 도메인 분석 및 모델링 (이벤트 스토밍)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;88&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 구현과 직접 관련이 있는 것은 아니지만 도메인 이벤트를 중심으로 비즈니스 도메인을 분석하는 기법으로&amp;nbsp;&lt;b&gt;이벤트 스토밍&lt;/b&gt;&amp;nbsp;이 있다. 이는 비즈니스 도메인에 대한 이해를 높이고 공유하고 모델링과 디자인에 활용하는 데 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;91&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트는 비즈니스 도메인에서 일어나는 중요한 결과이며 여기에서 거슬러 올라가 전체를 탐색해 나간다(이벤트 &amp;rarr; 이벤트를 일으킨 명령 &amp;rarr; 명령 실행 주체(액터, 정책) &amp;rarr; 액터가 의사결정을 위해 참조한 리드 모델).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;91&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-line=&quot;91&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E6%89%B1%E3%81%86%E3%81%9F%E3%82%81%E3%81%AE%E5%AE%9F%E8%A3%85&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;94&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;도메인 이벤트를 처리하기 위한 구현&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-line=&quot;96&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 생성해, 바운디드 컨텍스트내에서 처리하기 위해서 필요한 구현을 소개하도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;98&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;98&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트 발생 ~ 소비는 동기화 혹은 비동기.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;99&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 도메인 이벤트는 발생한 바운디드 컨텍스트 내에서 소비.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;100&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;100&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문맥 밖에 통지하고 싶은 경우는, 공개용으로 변환&amp;middot;정형한 후에 ​​통지하는 것이 일반적.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 도메인 이벤트 설계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;104&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트는 집약에 대한 작업(명령)의 결과로 발생한다. 예를 들어, &quot;주문&quot;이라는 집약에 대한 &quot;취소&quot; 작업은 집약 상태를 취소됨으로 변경하고 &quot;주문이 취소됨&quot;이라는 도메인 이벤트를 발생시킨다. 우선은 도메인 이벤트를 설계, 구현해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774958360083&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**「주문이 취소되었다」이벤트 */
data class OrderCancelled(
  // 일어난 일을 설명하기 위한 정보를 속성으로 가진다.
  val orderId: OrderId,
  val reason: String,
  val occurredAt: Instant,
  ...
) : DomainEvent&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;121&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포인트:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;123&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;123&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;과거에 발생한 사건이기 때문에 과거형으로 명명(예: OrderCancelled, ItemShipped)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;124&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비즈니스 도메인 단어에서 어떤 일이 발생했는지 이름에 정확하게 반영&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;125&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;과거에 발생한 사건이기 때문에 이뮤터블&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;126&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트는 도메인 모델의 일부&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AB%E6%8C%81%E3%81%9F%E3%81%9B%E3%82%8B%E6%83%85%E5%A0%B1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;128&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;도메인 이벤트에 제공하는 정보&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;130&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트에는 도메인에서 무슨 일이 일어났는지 설명하는 완전한 정보가 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;132&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;132&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 설명하는 데 필요한 모든 정보가 존재.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;133&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트가 암시적으로 의존하는 외부 정보 포함.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;134&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;134&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어, 세금 포함 가격을 계산하고 기록하는 이벤트라면, 그 계산에 사용한 소비세율의 스냅샷도 포함한다(장래 세율이 바뀌어도 계산 결과를 재현할 수 있도록)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;135&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그 외, 구독자가 필요로 하는 정보도 포함할 수도 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;136&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위의 내용을 충족시킨 후 필요한 최소한의 정보를 유지.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AB%E5%85%B1%E9%80%9A%E3%81%99%E3%82%8B%E6%83%85%E5%A0%B1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;138&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;모든 도메인 이벤트에 공통된 정보&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;140&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;모든 도메인 이벤트에 공통적으로 가져야 하는 정보가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;142&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;142&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집약 ID (필수) ... 이벤트는 집약에서 발생하므로 집약 식별자를 가짐.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;143&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 발생 시간 (필수) ... 과거의 이벤트이므로 발생 시간이 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;144&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 식별자 ... 비동기로 처리 할 때 이벤트 중복을 제외하는 데 유용함 (아래 참조).&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;145&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집계 버전 ... 이벤트를 지속할 때 낙관적 배타적 제어를 수행하거나 이벤트 순서를 보장하는 데 사용함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;147&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 항목은 공통 인터페이스에 정의하는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;147&quot; 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-line=&quot;147&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 도메인 이벤트 생성&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;147&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집약이 도메인 이벤트를 생성하고 발행하는 구현 방법으로 다음 세 가지 패턴이 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B31.-%E9%9B%86%E7%B4%84%E8%87%AA%E8%BA%AB%E3%81%8C-event-publisher-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E7%99%BA%E8%A1%8C%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;153&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 1. 집약 자체가 Event Publisher를 사용하여 도메인 이벤트를 발행&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;155&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집약 자체가 도메인 이벤트를 발행한다. &lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://amzn.asia/d/0eV44aXJ&quot;&gt;실천 도메인 구동 설계&lt;/a&gt;&amp;nbsp;등에서 소개되고 있는 패턴이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774958592598&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Order {
  fun cancel(reason: String) {
    check(canCancel())
    // 집약의 상태를 변화시킴
    this.status = OrderStatus.CANCELLED
    this.cancelReason = reason

    // 이트를 생성하고 발행함
    val event = OrderCancelled(id, reason, ...)
    DomainEventPublisher.publish(event)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;173&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(여기서는&amp;nbsp;DomainEventPublisher이벤트를 받아 디스패치하는 부품으로 사용하고 있다. 구현에 대해서는 후술하도록 하겠다)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;175&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 패턴은 구현은 간단합니다만, 아래의 문제가 있기 때문에 남용해서는 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;177&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;177&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집약이 이벤트 발행 처리에 의존하기 때문에 안정성과 시험 가능성이 떨어짐&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;178&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉시 디스패치되므로 집약을 지속하기 전에 핸들러가 이벤트를 처리할 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B32.-%E9%9B%86%E7%B4%84%E3%81%8C%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E4%BF%9D%E6%8C%81%E3%81%97%E3%80%81%E5%BE%8C%E3%81%A7%E5%8F%96%E3%82%8A%E5%87%BA%E3%81%97%E3%81%A6%E7%99%BA%E8%A1%8C%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;180&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 2. 집약이 도메인 이벤트를 유지하고 나중에 검색하고 게시&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;182&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 패턴이 가장 자주 소개되는 패턴이다 (Jimmy Bogard의&amp;nbsp;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/&quot;&gt;A better domain events pattern&lt;/a&gt;&amp;nbsp;등).&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774958848234&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Order {
  // 집약내에서 발생한 미발행의 이벤트를 보유할 필드
  // 공통의 기저 클래스를 가지도록 해도 좋을 것이다
  private val events = mutableListOf&amp;lt;DomainEvent&amp;gt;()

  fun cancel(reason: String) {
    check(canCancel())
    this.status = OrderStatus.CANCELLED
    this.cancelReason = reason

    // 생성한 이벤트를 events에 추가
    // 이 시점에서는 이벤트는 디스패치되지 않는다
    val event = OrderCancelled(id, reason, ...)
    events.add(event)
  }

  fun events(): List&amp;lt;DomainEvent&amp;gt; { // 외부에서 꺼낼 수 있도록 해둠
    return events.toList()
  }
}

// Use case
fun cancelOrder(orderId: OrderId, reason: String) {
  val order = orderRepository.findById(orderId)
  order.cancel(reason)
  orderRepository.save(order)

  // 생성된 이벤트를 발행
  // 리포지터리의 구현 내에 이를 구현하는 사람들도 있다(리포지터리의 책임 범위 밖이지만)
  domainEventPublisher.publish(order.events())
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B33.-%E9%9B%86%E7%B4%84%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%8C%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E8%BF%94%E3%81%99&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;218&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 3. 집계 명령이 이벤트를 반환합니다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;220&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집약에 대한 조작이 이벤트를 리턴하게 함으로써, 이벤트의 발행을 명시적으로 실시할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774958907522&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Order {
  fun cancel(reason: String): List&amp;lt;DomainEvent&amp;gt; {
    check(canCancel())
    this.status = OrderStatus.CANCELLED
    this.cancelReason = reason

    // 이벤트를 생성하고 반환
    val event = OrderCancelled(orderId, reason, ...)
    return listOf(event)
  }
}

// Use case
fun cancelOrder(orderId: OrderId, reason: String) {
  val order = orderRepository.findById(orderId)
  val events = order.cancel(reason)
  orderRepository.save(order)

  domainEventPublisher.publish(events) // 이벤트를 발행
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 이 패턴은 집약의 설계를 이뮤터블로 하는 경우에도 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774958938908&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Use case
fun cancelOrder(orderId: OrderId, reason: String) {
  val order = orderRepository.findById(orderId)
  val (cancelledOrder, events) = order.cancel(reason) // 변경 후의 집약과 이벤트를 반환
  orderRepository.save(cancelledOrder)

  domainEventPublisher.publish(events)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;3.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E5%87%A6%E7%90%86%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;262&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. 이벤트 처리&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;264&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DomainEventPublisher에서 발행된 이벤트를 구독하고 처리하는 구현 예를 살펴보자. 먼저 도메인 이벤트를 처리하기 위한 인터페이스로&amp;nbsp;DomainEventHandler정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774959023173&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface DomainEventHandler&amp;lt;T : DomainEvent&amp;gt; {
  fun eventType(): KClass&amp;lt;T&amp;gt; // 이 핸들러가 이벤트 형으로 반환
  fun handle(event: T) // 이벤트를 처리
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 다음에 이것을 구현한 클래스를 작성한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774959085971&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SendEmailWhenOrderCancelledHandler : DomainEventHandler&amp;lt;OrderCancelled&amp;gt; {
  override fun eventType() = OrderCancelled::class
  override fun handle(event: OrderCancelled) {
    // 이벤트에 반응하여 행할 액션을 기재
    // 이벤트 핸들러는 어플리케이션 층의 주인이며, 리포지터나 서비스를 이용하는 것이 가능
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;명명은&amp;nbsp;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://learn.microsoft.com/ja-jp/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation&quot;&gt;도메인 이벤트: 디자인 및 구현 - .NET | Microsoft Learn&lt;/a&gt;&amp;nbsp;예제를 따른다. (&amp;nbsp;${무엇을 할것인가}When${무엇이일어났을때}Handler) 하나의 이벤트에 대해 수행하려는 여러 작업이 있는 경우 일반적으로 처리기를 여러 개 만든다. (단일 책임의 원칙, 개방 폐쇄의 원칙)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4-1.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E5%90%8C%E6%9C%9F%E7%9A%84%E3%81%AA%E3%83%87%E3%82%A3%E3%82%B9%E3%83%91%E3%83%83%E3%83%81&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;291&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4-1. 이벤트의 동기 디스패치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;293&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마지막으로 발생한 이벤트를 핸들러에 디스패치하는&amp;nbsp;DomainEventPublisher구현한다. 이벤트의 디스패치를 ​​동기적으로 실시하는 경우와 비동기적으로 실시하는 경우가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;296&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선은 동기적인 디스패치의 구현 예는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774959239378&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class DomainEventPublisher {
  // 이벤트 타입마다 핸들러를 보유한다
  private val handlers = mutableMapOf&amp;lt;KClass&amp;lt;out DomainEvent&amp;gt;, List&amp;lt;DomainEventHandler&amp;lt;out DomainEvent&amp;gt;&amp;gt;&amp;gt;()

  // 이벤트 핸들러를 등록한다
  fun subscribe(handler: DomainEventHandler&amp;lt;out DomainEvent&amp;gt;) {
    val eventType = handler.eventType()
    handlers[eventType] = handlers.getOrDefault(eventType, emptyList()) + handler
  }

  // 발생한 이벤트 타입에 따라 핸들러에 디스패치한다
  // 이벤트에 대응할 핸들러를 순서대로 동기적으로 호출한다
  fun publish(events: List&amp;lt;DomainEvent&amp;gt;) {
    events.forEach { event -&amp;gt;
      handlers[event::class]?.forEach { handler -&amp;gt;
        (handler as DomainEventHandler&amp;lt;DomainEvent&amp;gt;).handle(event)
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 이벤트 핸들러는 애플리케이션이 시작될 때 등록된다. DI 컨테이너를 사용하는 경우 DI 컨테이너에 등록된 모든 핸들러를 자동으로 등록하는 것이 편리하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774959280584&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun registerDomainEventHandlers() { // 어플리케이션 기동시에 호출한다
  val handlers = applicationContext.getBeansOfType(DomainEventHandler::class.java) // Spring Framework の例
  handlers.values.forEach { handler -&amp;gt; domainEventPublisher.subscribe(handler) }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vupV9/dJMcahcOx07/1xnqklTCyvOnyoMP9mkcj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vupV9/dJMcahcOx07/1xnqklTCyvOnyoMP9mkcj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vupV9/dJMcahcOx07/1xnqklTCyvOnyoMP9mkcj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvupV9%2FdJMcahcOx07%2F1xnqklTCyvOnyoMP9mkcj0%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;1670&quot; height=&quot;923&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;923&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 id=&quot;4-2.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E9%9D%9E%E5%90%8C%E6%9C%9F%E7%9A%84%E3%81%AA%E3%83%87%E3%82%A3%E3%82%B9%E3%83%91%E3%83%83%E3%83%81&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;335&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4-2. 이벤트의 비동기 디스패치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;337&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 컨텍스트 (동일한 시스템) 내에서도 이벤트를 비동기적으로 처리하고 싶을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;339&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;339&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 소비하는 측의 처리를 기다리지 않기 때문에 응답성이 향상&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;340&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 소비하는 쪽이 다운되어도 처리를 계속할 수 있으므로 가용성이 향상&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;341&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;트랜잭션이 작아지고 데이터베이스 독점 제어 충돌이 삭감&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;343&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한편, 이벤트가 확실하게 처리되도록(듯이) 하기 위해서는 복잡한 구현이 필요하게 된다 (이벤트 중복, 순서 보증, 오류 처리, 보상 조치 등)&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E7%B5%90%E6%9E%9C%E6%95%B4%E5%90%88%E6%80%A7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;346&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;결과 무결성 정보&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;348&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 비동기적으로 처리하면 이벤트가 발생한 후 이벤트 핸들러 측 처리가 완료될 때까지 시스템 상태에 일시적으로 일관성이 없는 상태가 발생한다 (예: 상품 구입 처리와 포인트 부여 처리가 비동기로 행해지는 경우, 구입했는데 포인트가 미부여 상태가 일시적으로 발생).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;351&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 일시적인 불일치를 허용하고 시간이 지남에 따라 결국 일관성을 담보하는 설계를&amp;nbsp;&lt;b&gt;결과 무결성&lt;/b&gt;이라고한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고로 반대되는 단어는&amp;nbsp;&lt;b&gt;트랜잭션 무결성&lt;/b&gt;&amp;nbsp;이며 항상 무결성을 유지한다.&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;일관성 지연은 허용되지만 일관성 자체를 생략하는 것은 아니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;무결성이 취해질 때까지의 지연을 어느 정도 허용하는지는 비즈니스 요건에 따른다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결과 무결성을 확실하게 담보하기 위한 구현은 꽤 어렵다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 id=&quot;outbox-%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-%2B-%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%83%90%E3%82%B9%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E9%85%8D%E4%BF%A1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;362&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Outbox 패턴 + 메시지 버스를 통한 이벤트 전달&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;364&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Producer - Consumer간을 결과 무결성으로 한다고 해도, 「이벤트의 발생원(=집약)의 상태의 영속화」와 「이벤트가 공개되는 것」은 확실히 정합할 필요가 있다. (한쪽만 성공하는 것은 안되기 때문)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;366&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 경우 Outbox 패턴을 사용하는 것이 일반적이다. (다른 방법으로는 도메인 이벤트만을 영속화하고 집약은 이벤트에서 재구성하는 방법도 있다)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;369&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Outbox 패턴을 사용하면 집계를 저장하는 것과 동일한 트랜잭션에서 알림을 받고 싶은 이벤트를 일시적으로 유지하고 다른 프로세스가 이벤트를 가져와서 알리게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6wIg0/dJMcaiQh4l3/QuBarGXYdlw2jsIIPbwZ5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6wIg0/dJMcaiQh4l3/QuBarGXYdlw2jsIIPbwZ5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6wIg0/dJMcaiQh4l3/QuBarGXYdlw2jsIIPbwZ5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6wIg0%2FdJMcaiQh4l3%2FQuBarGXYdlw2jsIIPbwZ5k%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;1110&quot; height=&quot;425&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;383&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;383&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;알리고 싶은 이벤트를 영속화하기 위한 테이블(Outbox 테이블)을 준비한다&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;384&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동일한 트랜잭션 내에서 집약 지속성 및 이벤트 지속성을 수행하고 트랜잭션 커밋&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;385&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다른 프로세스(메세지 릴레이)가 Outbox 테이블을 모니터링하고 새 이벤트를 검색한다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;386&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메세지 릴레이는 취득한 이벤트를 메시지 버스(Kafka, RabbitMQ, etc.)에 투입한다&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;387&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성공하면 메세지 릴레이는 Outbox 테이블에서 이벤트를 삭제하거나 게시됨으로 표시한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;389&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5에서 실패하면 3-4가 다시 실행되므로 이벤트가 두 번 이상 전송될 수 있다(At-least-once)&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%83%AA%E3%83%AC%E3%83%BC%E3%81%AE%E5%AE%9F%E8%A3%85&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;391&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;메시지 릴레이 구현&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;393&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Outbox 테이블에 기록된 새 이벤트를 검색하는 방법에는 두 가지가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;395&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;395&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;폴링 ... 정기적으로 Outbox 테이블에 액세스하여 새 이벤트를 검색.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;396&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CDC(Change Data Capture) ... 어떠한 방식으로 데이터 변경 통지를 받음(DB에 따라서는 할 수 없는 경우도 존재)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;397&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;397&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;셀프 호스팅된 RDB라면&amp;nbsp;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://debezium.io/&quot;&gt;Debezium&lt;/a&gt;&amp;nbsp;등의 툴을 사용&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;398&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AWS의 경우,&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;399&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;399&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DynamoDB라면 DynamoDB Streams로 구현 가능&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;400&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PostgresSQL이면 논리 복제를 사용하여 구현 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E5%8F%97%E4%BF%A1%E5%81%B4%E3%81%AE%E5%AE%9F%E8%A3%85&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;402&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이벤트 수신자 구현&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;404&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메세지 버스로부터 이벤트를 취득해 핸들러에 건네줍다. 핸들러의 구현은 기본적으로 동기적인 경우와 동일하다. 그러나 이벤트 알림이 비동기화되어 다음과 같은 문제가 발생할 수 있으므로 이를 해결해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;409&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;409&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트가 중복되어 도착&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;410&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트가 순서에 따라 도착&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;411&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;핸들러가 이벤트 처리에 실패함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E9%87%8D%E8%A4%87%E6%8E%92%E9%99%A4&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;413&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이벤트 중복 제거&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;415&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 이벤트는 적어도 한 번(At-least-once) 통지된다(메시지 버스나 Outbox가 배송 과정에서 잠재적인 재시도를 수행하기 때문에). 따라서 받은 이벤트의 중복을 제거하는 메커니즘이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;418&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;418&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 핸들러의 처리를 멱등으로 설계해, 같은 이벤트가 복수회 처리되어도 결과가 변하지 않도록 한다&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;419&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각 이벤트에 고유 식별자를 부여하고 처리된 이벤트 ID를 기록합니다. 중복 이벤트가 도착하면 무시하도록 말이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E9%A0%86%E5%BA%8F%E4%BF%9D%E8%A8%BC&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;421&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;이벤트 순서 보증&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;423&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 비동기적으로 알리는 경우 이벤트가 소비자에게 도달하는 순서가 발생 순서와 일치하지 않을 수 있다. 기본적으로 동일한 집약에 관한 이벤트는 발생순으로 처리되도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;426&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;426&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 버스 순서 보증 기능 사용&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;427&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;427&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예: Kafka 파티션(파티션 키를 집계 ID로 설정)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;428&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;수신자의 이벤트 순서 재정렬&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%82%A8%E3%83%A9%E3%83%BC%E5%87%A6%E7%90%86&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;430&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오류 처리&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;432&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 수신 측에서 예기치 않게 오류가 발생할 수 있습니다. (네트워크 오류 등)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 다음과 같은 접근 방식을 취합니다. 구현 방법은 메시지 버스에 따라 다릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;435&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;435&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동 재시도(재시도 간격을 지수 백오프로 조정)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;436&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오류가 발생한 이벤트를 교착 상태 큐로 이동하고 나중에 다시 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E8%A3%9C%E5%84%9F%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;438&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;보상 액션&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;440&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 핸들러가 어떤 이유로 처리를 완료하지 못할 수 있다. 예를 들면 주문 이벤트에 응답하여 결제를 수행하는 처리기에서 지정된 결제 방법으로 결제할 수 없는 경우 등 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;443&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트는 이미 발생한 것을 설명하며 이벤트가 없었던 걸로 할 수는 없다. 이벤트를 뒤집는 유일한 방법은 이벤트의 효과를 상쇄하기 위한 추가 조치를 수행하는 것 이다. 앞의 예에서 '주문 취소 및 재고 되돌리기'와 같은 작업을 수행하는 보상 작업이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;443&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E5%A4%96%E9%83%A8%E3%81%AB%E9%80%9A%E7%9F%A5%E3%81%99%E3%82%8B%EF%BC%88%E7%B5%B1%E5%90%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%EF%BC%89&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;467&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;도메인 이벤트를 컨텍스트 외부에 알리기(통합 이벤트)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;469&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 컨텍스트의 연동을 위해 도메인 이벤트를 바운디드 컨텍스트 외부에 알리고 싶을 수 있다. 이벤트를 문맥내에 통지하는 경우와는 몇 가지 다른 점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E3%81%9D%E3%81%AE%E3%81%BE%E3%81%BE%E5%A4%96%E9%83%A8%E3%81%AB%E5%85%AC%E9%96%8B%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%AE%E6%98%AF%E9%9D%9E&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;472&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;도메인 이벤트를 그대로 외부에 공개하는 것&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;474&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우선 도메인 이벤트를 외부에 그대로 공개해도 좋은가 하는 문제가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;476&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;476&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트는 도메인 모델의 일부이며 바운디드 컨텍스트의 내부 표현이다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;477&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;477&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부에 공개하면 캡슐화가 무너진다&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;478&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부 표현과 외부 시스템이 결합하면 이벤트 구조 변경이 어려워진다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;479&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부와의 협력에 필요한 정보와 도메인 이벤트 정보가 일치하지 않을 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;481&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 이유로 일반적으로 도메인 이벤트를 그대로 외부에 게시하는 것은 피해야 하지만, 책이나 기사에 따라서는 그대로 공개하는 실장예를 소개하고 있는 것도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;%E5%A4%89%E6%8F%9B%E3%81%97%E3%81%A6%E5%85%AC%E9%96%8B%E3%81%99%E3%82%8B%E6%B4%BE&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;483&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;변환하고 공개하는 구현&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;485&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 외부 컨텍스트로 전달하기 전에 게시 이벤트로 변환한다. (이후 통합 이벤트(&amp;nbsp;&lt;b&gt;Integration Event&lt;/b&gt;&amp;nbsp;)라고 한다.)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변환에는 상태 비저장 변환과 상태 저장 변환이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;488&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;488&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스테이트리스 변환(=도메인 이벤트만을 출처로 한다)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;489&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;489&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;불필요한 메시지와 속성을 생략&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;490&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;490&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컨텍스트 간의 우발적인 결합을 피하기 위해&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;491&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보안을 위해&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;492&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;연계에 적합한 형식이나 명칭으로 변환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;493&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태 저장 변환 (= 과거 도메인 이벤트 및 다른 데이터 스토어에서 얻은 정보 사용)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;494&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;494&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 이벤트 집계 및 통합&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;495&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;리포지토리에서 얻은 정보 추가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관련 예나 블로그 포스트는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-line=&quot;499&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;499&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://learn.microsoft.com/ja-jp/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation&quot;&gt;도메인 이벤트: 디자인 및 구현 - .NET | Microsoft Learn&lt;/a&gt;&amp;nbsp;은 도메인 이벤트를 통합 이벤트(Integration Event)로 변환하여 외부에 게시한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;500&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;500&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 변환은 DomainEventHandler에서 수행하고 통합 이벤트 전송 서비스에 전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;501&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;속성은 도메인 이벤트와 동일하지 않으며 필요에 따라 추가 및 삭제된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;502&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://learning.oreilly.com/library/view/learning-domain-driven-design/9781098100124/&quot;&gt;Learning Domain-Driven Design&lt;/a&gt;&amp;nbsp;에서는 도메인 이벤트를 공개 언어로 변환하여 외부에 게시한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;503&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;503&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 전달하기 전에 프록시를 통해 변환&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;504&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;504&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스테이트리스 모델 변환&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;505&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태 저장 모델 변환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;506&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트 및 기타 이벤트를 구분하는 것이 좋다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;507&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;507&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Event notification ... 사건의 발생을 통지하지만, 상세 정보는 가지지 않는다. 자세한 정보가 필요한 경우 소비자로부터 명시적으로 문의&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;508&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Event-carried state transfer ... 컨텍스트간에 데이터 복제를 동기화하기 위해 업스트림 컨텍스트의 데이터 변경을 다른 컨텍스트로 전송&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;509&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Domain event ... 도메인 내에서 발생한 사건을 설명하는 메시지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;그대로 공개하는 구현&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 이벤트를 변환하는 데 어려움이 있다면 여기를 선택하는 것도 가능하다. 다만 위에서 언급한 문제가 있으므로 주의가 필요가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-line=&quot;520&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;520&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://amzn.asia/d/0eV44aXJ&quot;&gt;실습 도메인 구동 설계&lt;/a&gt;&amp;nbsp;에서는 JSON 직렬화된 도메인 이벤트를 외부에 통지하는 예가 소개되어 있다&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-line=&quot;521&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;521&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 내에서 발생한 모든 도메인 이벤트를 받는 핸들러 만들기&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;522&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 핸들러는 수신한 이벤트를 JSON으로 직렬화하여 이벤트 스토어(RDB의 1 테이블)에 영속화&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;523&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 스토어에 저장된 이벤트는 다음 방법 중 하나로 외부에 통지&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;524&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;524&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트를 얻기 위해 RESTful API를 공개하고 외부 서비스에서 폴링&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;525&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시징 미들웨어로 전송&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;526&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://amzn.asia/d/0bdNXgEr&quot;&gt;마이크로서비스 패턴&lt;/a&gt;&amp;nbsp;에서도 도메인 이벤트를 외부에 알리는 예가 소개되어 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;527&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;527&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원래 도메인 이벤트를 외부와의 연계를 위한 것으로 설계하고 있다&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;528&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;'이벤트 인리치먼트'라고 하여 외부 서비스가 요구하는 추가 정보를 도메인 이벤트에 포함시키는 설계를 소개하고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%E7%B5%B1%E5%90%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E8%A8%AD%E8%A8%88&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;532&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;통합 이벤트 설계&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;534&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트는 &quot;도메인상의 사건을 정확하게 설명하는 과부족한 정보&quot;라는 관점에서 설계된다. 한편, 통합 이벤트는 바운디드 컨텍스트의 공개 I/F의 일부이며, 「외부 컨텍스트와의 제휴를 위한 계약」이라고 하는 성질이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #000000; text-align: start; border-collapse: collapse; width: 100%; height: 68px;&quot; border=&quot;1&quot; data-line=&quot;536&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;통합 이벤트&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot; data-line=&quot;538&quot;&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인의 사건을 설명하는 정보&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 컨텍스트와의 연계를 위한 계약&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot; data-line=&quot;539&quot;&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;경계화된 컨텍스트 내에서 소비&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;경계가 붙은 컨텍스트 외부에 게시&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot; data-line=&quot;540&quot;&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 처리는 동기화 또는 비동기일 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #ffffff; height: 17px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;항상 비동기적으로 처리됨&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;%E5%A5%91%E7%B4%84%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AE%E7%B5%B1%E5%90%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;543&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;계약으로서의 통합 이벤트&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;545&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://amzn.asia/d/0j03Un9r&quot;&gt;함수형 도메인 모델링&lt;/a&gt;&amp;nbsp;에는 두 가지 컨텍스트가 계약에 동의하는 패턴으로 다음 세 가지가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;547&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;547&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;공유 커널&lt;/b&gt;&amp;nbsp;관계 ... 컨텍스트간에 도메인 디자인을 공동 소유한다. 이벤트의 정의는 다른 공동 소유자와의 협의에 기초.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;548&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;고객 / 공급자&lt;/b&gt;&amp;nbsp;관계 ... 다운 스트림 컨텍스트가 업스트림 컨텍스트에 대해 원하는 계약을 정의한다 (&amp;nbsp;&lt;b&gt;소비자 중심 계약&lt;/b&gt;&amp;nbsp;). 이 계약을 충족하는 한 각 컨텍스트는 독립적으로 발전할&amp;nbsp; 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;549&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;순응자&lt;/b&gt;&amp;nbsp;관계 ... 하류 컨텍스트는 업스트림 컨텍스트가 제공하는 계약을 수락합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;소비자 구동 계약은 이벤트뿐만 아니라 마이크로 서비스 간의 I / F 설계에 사용되는 일반적인 패턴이다.계약을 끊지 않았음을 소비자와 공급자 모두가 테스트하여 통신이 끊어지지 않도록 보장할 수 있다. 계약을 테스트하는 도구로&amp;nbsp;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://docs.pact.io/&quot;&gt;Pact&lt;/a&gt;&amp;nbsp;가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%9E%E9%80%B2%E5%8C%96&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;554&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;스키마 진화&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;556&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트의 스키마는 비즈니스 요구 사항의 변경 등에 따라 변경된다. 이벤트의 생산자와 소비자가 서로 독립적으로 진화할 수 있고 모든 시점에서 중단없이 협력할 수 있도록 하려면 스키마 호환성을 고려해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;559&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;호환성 유형:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;561&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;561&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;정방향 호환성&lt;/b&gt;&amp;nbsp;: 새 스키마 데이터를 이전 스키마로 읽을 수 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;562&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;562&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생산자 측에서 새 스키마 이벤트를 제출해도 소비자 측은 코드를 변경할 필요가 없음.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;563&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;소비자는 새로운 스키마 정보가 필요한 경우에만 코드를 수정.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;564&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;역호환성&lt;/b&gt;&amp;nbsp;: 이전 스키마 데이터를 새 스키마로 읽을 수 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;565&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;565&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스키마가 미리 정의된 경우 소비자는 생산자보다 먼저 코드 업데이트를 릴리스할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;566&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 이벤트를 재처리해야 하는 경우에 대응 가능.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;567&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;완전 호환성&lt;/b&gt;&amp;nbsp;: 전방 호환성과 후방 호환성 모두&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;568&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;568&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가능한 한 목표로 삼아야함.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;569&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;호환성 요구 사항을 나중에 푸는 것은 쉽지만 엄격하게하기는 어려움.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;571&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파괴적 변경이 필요한 경우에는 오래된 이벤트와 새 이벤트를 모두 지원하고 마감일을 사용하여 이전 이벤트를 폐지하는 것이 일반적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;571&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%E7%B5%B1%E5%90%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%B8%E3%81%AE%E5%A4%89%E6%8F%9B%E3%81%A8%E9%80%81%E4%BF%A1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;573&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;통합 이벤트로 변환 및 전송&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;575&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트의 경우와 달리 통합 이벤트는 항상 비동기적으로 통지 및 처리된다. 통합 이벤트를 처리하는 메커니즘은 도메인 이벤트의 메커니즘을 기반으로 구축한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B31.-%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9%E3%83%BC%E3%81%A7%E7%B5%B1%E5%90%88%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AB%E6%98%8E%E7%A4%BA%E7%9A%84%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%97%E3%81%A6%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;578&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 1. 도메인 이벤트 핸들러에서 통합 이벤트로 명시적으로 변환 및 전송&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;580&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 받은 핸들러가 통합 이벤트로 변환하여 전송한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;582&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;582&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대상 도메인 이벤트를 구독하는 핸들러 만들기&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;583&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 핸들러 내에서 도메인 이벤트에서 통합 이벤트로 변환&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;584&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 통합 이벤트를 Outbox 패턴 및 메시지 버스를 사용하여 비동기 적으로 전송&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1774961029282&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// OrderCancelled 이벤트를 통합 이벤트로 변환 송신하는 핸들러
class PublishIntegrationEventWhenOrderCancelledHandler(
  private val integrationEventPublisher: IntegrationEventPublisher
) : DomainEventHandler&amp;lt;OrderCancelled&amp;gt; {
  override fun eventType() = OrderCancelled::class

  override fun handle(domainEvent: OrderCancelled) {
    // 통합 이벤트로 변환
    // - 연계를 통해 정보를 변환한다
    // - 불필요한 정보는 생략한다
    // - 리포지터리를 사용하여 추가 정보를 획득한다
    // - 여러 이벤트를 집약, 통합
    val integrationEvent = OrderCancelledIntegrationEvent(
      domainEvent.orderId,
      ...
    )
    // IntegrationEventPublisher는 Outbox 패턴 &amp;amp; 메세지를 이용한 비동기적으로 이벤트를 송신한다
    // （도메인 이벤트를 비동기로 디스패치할 경우와 같이 동일한 기술을 사용하여 구현한다）
    integrationEventPublisher.publish(integrationEvent)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 위 예제에서는 도메인 이벤트를 개별적으로 처리하지만 모든 도메인 이벤트를 중앙 집중식으로 처리하는 핸들러를 만들 수도 있다. 단일 책임의 원칙에 반하는 것 같지만 편한 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774961060917&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PublishIntegrationEventHandledHandler(
  private val integrationEventPublisher: IntegrationEventPublisher
) : DomainEventHandler&amp;lt;DomainEvent&amp;gt; {
  override fun eventType() = DomainEvent::class

  override fun handle(domainEvent: DomainEvent) {
    val integrationEvent = when (domainEvent) {
      is OrderCancelled -&amp;gt; OrderCancelledIntegrationEvent(
        domainEvent.orderId,
        ...
      )
      is OrderShipped -&amp;gt; OrderShippedIntegrationEvent(
        domainEvent.orderId,
        ...
      )
      ...
    }
    integrationEventPublisher.publish(integrationEvent)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B32.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E5%A4%89%E6%8F%9B%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%82%92%E9%85%8D%E7%BD%AE%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;635&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 2. 이벤트 변환 레이어 배치&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;637&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 이벤트를 비동기적으로 보내는 메커니즘이 있는 경우 어딘가에서 이벤트 흐름을 가로채고 통합 이벤트로 변환할 수 있다. 이벤트 변환 레이어를 배치하는 장소로는 다음과 같은 후보들이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;640&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;640&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Outbox 테이블에서 도메인 이벤트를 검색한 직후 통합 이벤트로 변환하여 메시지 버스에 입력&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;641&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 버스에 투입된 도메인 이벤트를 검색하여 통합 이벤트로 변환한 후 다시 메시지 버스에 투입&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;642&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 버스에 따라서는 변환 처리를 실시하기 위한 후크를 제공하고 있는 경우가 있다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;643&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;643&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AWS Kinesis Data Firehose, Kafka Connect 등&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B33.-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8Bapi%E3%82%92%E6%8F%90%E4%BE%9B%E3%81%97%E3%80%81%E8%B3%BC%E8%AA%AD%E5%81%B4%E3%81%8B%E3%82%89%E3%83%9D%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;645&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패턴 3. 이벤트를 검색하는 API를 제공하고 구독자로부터 폴링&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;647&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전혀 다른 접근법으로는 이벤트를 발행하는 측이 이벤트 취득의 엔드 포인트를 공개하고 이벤트를 수신하는 측에서 폴링하는 방법도 있다. 이 방법은&amp;nbsp;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://amzn.asia/d/0eV44aXJ&quot;&gt;실습 도메인 기반 설계&lt;/a&gt;&amp;nbsp;에서 &quot;RESTful 리소스를 통한 알림 발행&quot;이라는 타이틀로 소개되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 발행자:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;651&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;651&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 내에서 발생한 모든 이벤트를 이벤트 저장소에 영속화&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;652&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로운 이벤트를 얻기 위한 엔드포인트 준비&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;653&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;653&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;RSS 및 Atom 피드와 같은 이미지&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;654&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부에서는 이벤트 스토어에서 이벤트를 검색하고 반환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;650&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;655&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 수신자:&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;656&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;656&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 발행자의 엔드포인트를 정기적으로 폴링&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;657&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어디까지 처리했는지 직접 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;659&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;통합 이벤트로의 변환은, 이벤트 스토어에의 기입시 또는, 응답을 돌려주기 전에 실시하면 좋을 것이다. (서적에서는 변환 처리는 생략되어 있다)&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://zenn.dev/kohii/articles/4a68e768c93573&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://zenn.dev/kohii/articles/4a68e768c93573&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/459</guid>
      <comments>https://engineer-mole.tistory.com/459#entry459comment</comments>
      <pubDate>Tue, 31 Mar 2026 21:46:06 +0900</pubDate>
    </item>
    <item>
      <title>Node.js 버전 관리 툴 Volta</title>
      <link>https://engineer-mole.tistory.com/458</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Volta란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-origin-width=&quot;953&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odT3w/dJMcabQVPSw/z56lMHrEKve7S0VKfyGyTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odT3w/dJMcabQVPSw/z56lMHrEKve7S0VKfyGyTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odT3w/dJMcabQVPSw/z56lMHrEKve7S0VKfyGyTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FodT3w%2FdJMcabQVPSw%2Fz56lMHrEKve7S0VKfyGyTk%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;953&quot; height=&quot;334&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp;Volta&lt;/b&gt;&amp;nbsp;는&amp;nbsp;&lt;b&gt;JavaScript 도구 관리툴이다&lt;/b&gt;. 타이틀에서는 Node.js 의 버전 관리툴로 소개하고 있지만, npm&amp;middot;yarn 의 버전 관리도 할 수 있다. &lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://volta.sh/&quot;&gt;공식 사이트&lt;/a&gt;&amp;nbsp;에서는 &quot;The Hassle-Free JavaScript Tool Manager(수고 없는 JavaScript 도구 관리자)&quot;라고 소개되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;팀의 Node.js 버전 관리를 Volta에 통합한 결과 DX가 높아져 Volta 장점을 느끼게 됐다.이 포스트에서는 개발자의 Volta 인구를 늘리기 위해 Volta를 소개하고 사용하는 방법에 대해 설명하고자한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;15&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-line=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;15&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Volta 개요&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 공식 사이트에서 소개된 Volta의 3가지 특징을 살펴보도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UYiU1/dJMcadA9pTW/iuyK4lHntWdLdopQPp0jkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UYiU1/dJMcadA9pTW/iuyK4lHntWdLdopQPp0jkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UYiU1/dJMcadA9pTW/iuyK4lHntWdLdopQPp0jkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUYiU1%2FdJMcadA9pTW%2FiuyK4lHntWdLdopQPp0jkK%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;1069&quot; height=&quot;179&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;Fast : Volta는 Rust로 제작되었으므로 다른 버전 관리 도구보다 더 빠르다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Reliable : 개인적으로 가장 장점을 부분으로 volta pin라는 명령을 사용하면 프로젝트 멤버의 Node.js 및 npm 버전을 쉽게 맞출 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Universal :&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;Windows &amp;middot; Mac &amp;middot; Linux. 모든 OS에서 작동한다.&amp;nbsp;&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;Volta의 특징&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;background-color: #ffffff; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;Volta는 Node 엔진을 한 번 선택하면 그 이후에는 걱정할 필요가 없다. 프로젝트를 전환한다고 해도 Node를 수동으로 전환할 필요가 없다. 정기적으로 다시 설치하거나 왜 작동하지 않는지 확인하지 않고도 툴체인에 npm 패키지 바이너리를 설치할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음의 커맨드 하나로 특정 버전의 노드를 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773230433806&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta install node@14&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그리고 공동 작업자를 위한 재현 가능한 환경을 커맨드 하나로 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773230465925&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta pin node@12&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;위 커맨드를 실행하면 Node 엔진의 버전을 package.json에 저장, 그 내용을 git에 커밋 할 수 있다. 따라서 프로젝트 디렉토리에서 Node를 실행할 때마다 Volta는 자동으로 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;package.json에 설정된&lt;/span&gt; 버전과 동일한 버전의 Node로 전환한다. 이로 인해 공동 연구자는 각 개발 머신에 Volta를 설치하여 동일한 환경을 구현할 수 있다. &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;h2 id=&quot;volta-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%82%88%E3%81%86-%EF%BD%9C-getting-started&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;106&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Volta 설치하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 id=&quot;%E4%B8%80%E6%97%A6-node.js%E3%83%BBnpm-%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;110&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;먼저 Node.js &amp;middot; npm 삭제&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 설치하기 전에 현재 사용중인 Node 버전 관리 도구를 제거하고 Node &amp;middot; npm을 사용할 수없는 상태로 만드는 것이 좋다. 툴&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; 삭제에 관해서는 환경에 따른 패턴이 너무 많기 때문에 (nodebrew, NVM, nodist, ...) 삭제 방법에 대해서는 생략하도록 하겠다. 자신의 환경에 맞는 삭제 방법을 조사 후 실행하길 바란다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;volta-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;119&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Volta 설치&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;121&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a style=&quot;color: #0f83fd;&quot; href=&quot;https://docs.volta.sh/guide/getting-started&quot;&gt;공식 Getting Started&lt;/a&gt;&amp;nbsp;에 따라 설치하면 된다. OS가 Mac / Linux (WSL 포함)이면 다음 커맨드로 쉽게 설치할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773230793978&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl https://get.volta.sh | bash&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;133&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Windows는 Windows Installer를 사용할 수 있으므로 공식 설명서에 따라 다운로드하여 실행하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;133&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;설치 확인&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;133&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; .zshrc파일등의 쉘의 설정 파일을&amp;nbsp;cat커멘드등으로 파일 내용을 살펴자. 이하의 패스가 통하고 있으면 OK다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773230858353&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export VOLTA_HOME=&quot;$HOME/.volta&quot;
export PATH=&quot;$VOLTA_HOME/bin:$PATH&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그리고 터미널을 통해 volta 명령어를 사용할 수 있는지 확인해보자. 버전이 뜬다면 무사히 설치가 된 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773230884338&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta --version&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;툴 체인 관리&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt; Volta 툴체인이 관리하는 툴은&amp;nbsp;volta install과&amp;nbsp;volta uninstall 이 두 가지 커맨드로로 제어되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 특정 버전을 설정하여 Node의 기본 버전을 선택할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231017707&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta install node@14.15.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음과 같이 정확한 버전을 지정하지 않는 경우 Volta쪽에서 적절한 버전을 선정하여 설치하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231046485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta install node@14&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그냥 최신 버전을 다운로드 받고 싶다면 &lt;span style=&quot;text-align: start;&quot;&gt;latest를 사용하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231085354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ volta install node@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;앞서 말했듯 node이외에도 npm과 Yarn도 install 커맨드로 설치할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;프로젝트 관리&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; Volt의 특징에서 설명했듯 공동 작업자 팀 또는 커뮤니티 프로젝트에 사용할 개발 도구를 표준화 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;node-%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3%E3%81%AE%E5%9B%BA%E5%AE%9A&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-line=&quot;211&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Node 엔진 버전&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; volta pin명령을 사용하여 프로젝트의 Node 엔진 및 패키지 관리자 버전을 선택할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231193910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Node의 버전 고정
$ volta pin node@14.17

# npm의 버전 고정
$ volta pin npm@6.14&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;pin 명령어를 사용하면 Volta는 이를&amp;nbsp;package.json저장하므로 선택한 도구를 버전 관리에 커밋할 수 있게 된다. 즉, package.json에 다음과 같은 내용이 추가되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231239988&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;volta&quot;: {
  &quot;node&quot;: &quot;14.17.3&quot;,
  &quot;npm&quot;: &quot;6.14.13&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 이 방법으로 Volta를 사용하여 프로젝트를 수행하는 모든 사람이 선택한 버전과 동일한 버전을 자동으로 가져 오게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231260319&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;node --version # 14.17.3
npm --version # 6.14.13&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Volta 커맨드 옵션&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt; 공식 커맨드 레퍼런스는&amp;nbsp;&lt;a style=&quot;background-color: #ffffff; color: #0f83fd; text-align: start;&quot; href=&quot;https://docs.volta.sh/reference/&quot;&gt;이쪽&lt;/a&gt; 에서 확인 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773231497808&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이용 방법
volta [FLAGS] [SUBCOMMAND]

# FLAGS
    --verbose   # 상세진단을 유효화
    --quiet     # 불필요한 출력을 없앰
-v, --version   # Volta의 현재 버전 표시
-h, --help      # 헬프정보 표시


# SUBCOMMANDS
fetch          # 로컬 머신에 툴을 패치
install        # 툴을 툴 체인에 설치
uninstall      # 툴을 툴 체인에서 삭제
pin            # 프로젝트의 런타임이나 패키지 매니저를 고정
list           # 현재 툴 체인을 표시
completions    # Volta컴플리트를 생성
which          # Volta가 호출한 실제 바이너리를 특정
setup          # 현재 유저/쉘에 대해 Volta를 유효화
help           # 메시지 혹은 지정된 서브 커맨드의 헬프 표시&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://zenn.dev/taichifukumoto/articles/how-to-use-volta&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://zenn.dev/taichifukumoto/articles/how-to-use-volta&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/458</guid>
      <comments>https://engineer-mole.tistory.com/458#entry458comment</comments>
      <pubDate>Wed, 11 Mar 2026 21:19:02 +0900</pubDate>
    </item>
    <item>
      <title>의지를 구현하는 아키텍처 모더나이제이션</title>
      <link>https://engineer-mole.tistory.com/457</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;h1 data-section-id=&quot;1cbju28&quot; data-end=&quot;239&quot; data-start=&quot;205&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1.&amp;nbsp; AI 시대에 엔지니어의 역할은 무엇인가&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;273&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;최근에는 &lt;b&gt;AI가 코드를 작성하는 시대&lt;/b&gt;가 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;296&quot; data-start=&quot;275&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;과거 &amp;rarr; 개발자가 코드를 직접 작성&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;현재 &amp;rarr; AI가 코드를 생성&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;344&quot; data-start=&quot;317&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 개발자의 역할은 점점 다음으로 이동하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;407&quot; data-start=&quot;346&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;6lja16&quot; data-end=&quot;362&quot; data-start=&quot;346&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 기술을 선택할 것인가&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;uwm57n&quot; data-end=&quot;384&quot; data-start=&quot;363&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템을 어떤 구조로 설계할 것인가&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;kqm4i3&quot; data-end=&quot;407&quot; data-start=&quot;385&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 시스템이 어떤 가치를 만들 것인가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;410&quot; data-start=&quot;409&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;466&quot; data-start=&quot;412&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;466&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;ldquo;코드를 작성하는 것&amp;rdquo;보다&lt;br /&gt;&amp;ldquo;어떤 시스템을 만들 것인가를 결정하는 것&amp;rdquo;이 중요해진다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;521&quot; data-start=&quot;468&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;521&quot; data-start=&quot;468&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;x35flb&quot; data-end=&quot;559&quot; data-start=&quot;528&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;x35flb&quot; data-end=&quot;559&quot; data-start=&quot;528&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 레거시 시스템 문제 (Legacy System)&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;609&quot; data-start=&quot;561&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;레거시 시스템(legacy system) &lt;/b&gt;은 오래되어 구조가 복잡해진 시스템을 의미한다. 이러한 시스템들은 보통 다음 특징을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-section-id=&quot;1hrlj9m&quot; data-end=&quot;642&quot; data-start=&quot;636&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;644&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;변경이 어렵다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;644&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조금만 수정해도 다른 기능이 망가질 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;644&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발 속도가 느리다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;644&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코드가 복잡해서 수정하기 어려움&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;751&quot; data-start=&quot;720&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;운영이 어렵다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;751&quot; data-start=&quot;720&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제가 생기면 원인을 찾기 힘듦&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;761&quot; data-start=&quot;753&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런 상태에서는 아래의 것들이 어려워진다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;791&quot; data-start=&quot;763&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;1rhr2cp&quot; data-end=&quot;774&quot; data-start=&quot;763&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로운 기능 추가&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1igezxg&quot; data-end=&quot;783&quot; data-start=&quot;775&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 확장&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;62cyu1&quot; data-end=&quot;791&quot; data-start=&quot;784&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기술 도입&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;846&quot; data-start=&quot;807&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 레거시 시스템의 경우 &lt;b&gt;시스템을 현대화(modernization)&lt;/b&gt; 해야할 필요가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h1 data-section-id=&quot;1vkaxg1&quot; data-end=&quot;877&quot; data-start=&quot;853&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;1vkaxg1&quot; data-end=&quot;877&quot; data-start=&quot;853&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. 아키텍처 모더나이제이션이란 무엇인가&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;927&quot; data-start=&quot;879&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;아키텍처(Architecture) &lt;/b&gt;는 시스템의 &lt;b&gt;전체 구조와 설계 방식 &lt;/b&gt;라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;934&quot; data-start=&quot;929&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어 다음의 것들을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;984&quot; data-start=&quot;936&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;1oscwzn&quot; data-end=&quot;951&quot; data-start=&quot;936&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스가 어떻게 나뉘는지&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1bot0kv&quot; data-end=&quot;967&quot; data-start=&quot;952&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터가 어떻게 흐르는지&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1eue6v&quot; data-end=&quot;984&quot; data-start=&quot;968&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans 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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그렇다면 모더나이제이션은 무엇인가. 오래된 시스템을 &lt;b&gt;현재 환경에 맞게 바꾸는 것을 의미한다. &lt;/b&gt;즉, &lt;b&gt;아키텍처 모더나이제이션 = &lt;/b&gt;&lt;b&gt;시스템 구조를 새롭게 설계&lt;/b&gt;하는 것이다. 하지만 이는&lt;u&gt; 단순한 기술 교체가 아니다.&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1165&quot; data-start=&quot;1160&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어 다음과 같은 것으로 문제가 해결되지 않을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1205&quot; data-start=&quot;1167&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;2ji7vd&quot; data-end=&quot;1181&quot; data-start=&quot;1167&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버를 클라우드로 이동&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;ps1h38&quot; data-end=&quot;1192&quot; data-start=&quot;1182&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프레임워크 교체&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1k3q9q1&quot; data-end=&quot;1205&quot; data-start=&quot;1193&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마이크로서비스 도입&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1243&quot; data-start=&quot;1239&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜냐하면 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;시스템 문제는 &lt;/span&gt;&lt;b&gt;기술이 아니라 구조와 조직 문제일 수 있기 때문이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 data-section-id=&quot;drynmi&quot; data-end=&quot;1331&quot; data-start=&quot;1298&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;drynmi&quot; data-end=&quot;1331&quot; data-start=&quot;1298&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;drynmi&quot; data-end=&quot;1331&quot; data-start=&quot;1298&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 소시오테크니컬 관점 (Socio-technical)&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;1421&quot; data-start=&quot;1358&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;소시오테크니컬(Socio-technical) 은 &lt;/b&gt;&amp;ldquo;사회(조직)&amp;rdquo; + &amp;ldquo;기술&amp;rdquo;이 함께 영향을 준다는 개념으로 정리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1424&quot; data-start=&quot;1423&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉 아래의 것들이 모두 연결되어 있다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1457&quot; data-start=&quot;1426&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;1fkbs05&quot; data-end=&quot;1433&quot; data-start=&quot;1426&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조직 구조&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;ac7hg4&quot; data-end=&quot;1440&quot; data-start=&quot;1434&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팀 구조&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1rmdaxo&quot; data-end=&quot;1448&quot; data-start=&quot;1441&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발 방식&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;ozdjqo&quot; data-end=&quot;1457&quot; data-start=&quot;1449&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 구조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1485&quot; data-start=&quot;1480&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어 팀 구조가 다음과 같은 경우&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;A팀&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;B팀&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;C팀&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1537&quot; data-start=&quot;1518&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템도 보통 이런 구조가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 A&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 B&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 C&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1569&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;시스템 구조는 &lt;/span&gt;&lt;b&gt;조직 구조를 반영한다고 할 수 있으며,&amp;nbsp; &lt;/b&gt;이것을 흔히 &lt;b&gt;Conway&amp;rsquo;s Law&lt;/b&gt;라고 합니다. 따라서 &lt;b&gt;시스템을 바꾸려면 조직도 함께 바뀌어야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1569&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1569&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1569&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;6hm505&quot; data-end=&quot;1699&quot; data-start=&quot;1670&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;5. 비즈니스 이해 &amp;ndash; Event Storming&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;1724&quot; data-start=&quot;1701&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아키텍처를 바꾸기 전에 먼저 해야 할 것은 &lt;b&gt;비즈니스가 어떻게 동작하는지 이해하는 것이다&lt;/b&gt;. 여기서 사용할 수 있는 방법은 &lt;b&gt;Event Storming 이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-section-id=&quot;1xsm62l&quot; data-end=&quot;1820&quot; data-start=&quot;1802&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Event Storming&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1860&quot; data-start=&quot;1822&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 것은 비즈니스에서 발생하는 사건(event)을 정리하는 하나의 방법이다. 예를 들어 살펴보자. 쇼핑몰 서비스가 존재한다고 하자. 비즈니스에서 실제로 발생하는 사건을 나열하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상품 등록됨&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상품 주문됨&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결제 완료됨&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배송 시작됨&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배송 완료됨&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1952&quot; data-start=&quot;1918&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Event Storming &lt;/b&gt;은 이렇게 &lt;b&gt;비즈니스에서 실제로 발생하는 사건을 나열&lt;/b&gt;합니다.이 작업을 통해 서비스 흐름, 시스템 경계, 중요한 도메인을 발견할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1952&quot; data-start=&quot;1918&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1952&quot; data-start=&quot;1918&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;166jft7&quot; data-end=&quot;2033&quot; data-start=&quot;2013&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;6. 도메인 주도 설계 (DDD)&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;2088&quot; data-start=&quot;2035&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Event Storming으로 이해한 비즈니스 구조를 기반으로 다음 단계에서 사용하는 방법은&amp;nbsp;&lt;b&gt;DDD (Domain Driven Design)&lt;/b&gt; 이다. &lt;b&gt;도메인(domain) &lt;/b&gt;은 서비스가 해결하려는 &lt;b&gt;비즈니스 영역&lt;/b&gt;으로 정의할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2088&quot; data-start=&quot;2035&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;위에서 살펴보았던 예를 다시 인용하도록 하겠다. &amp;nbsp;Event Storming으로 사건을 정리한 후 필요한 기능을 정리하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2172&quot; data-start=&quot;2171&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2198&quot; data-start=&quot;2174&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;1pb5zvk&quot; data-end=&quot;2181&quot; data-start=&quot;2174&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주문 관리&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;15zfrls&quot; data-end=&quot;2190&quot; data-start=&quot;2182&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결제 시스템&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;6klnh1&quot; data-end=&quot;2198&quot; data-start=&quot;2191&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배송 관리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2208&quot; data-start=&quot;2200&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DDD의 핵심은 &lt;b&gt;비즈니스 구조를 중심으로 시스템을 설계한다하는 것이다. &lt;/b&gt;그래서 시스템을 다음과 같이 나눌 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주문 도메인&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결제 도메인&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;배송 도메인&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2303&quot; data-start=&quot;2297&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 비즈니스 구조를 중심으로 시스템을 설계하게 되면 시스템 이해가 쉬워지고 변경이 쉬워진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2303&quot; data-start=&quot;2297&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2303&quot; data-start=&quot;2297&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;1esy7oy&quot; data-end=&quot;2369&quot; data-start=&quot;2339&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;1esy7oy&quot; data-end=&quot;2369&quot; data-start=&quot;2339&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;7. 팀 구조 설계 &amp;ndash; Team Topologies&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;2382&quot; data-start=&quot;2371&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음으로 중요한 것은 &lt;b&gt;팀 구조&lt;/b&gt;이다. &lt;b&gt;Team Topologies&lt;/b&gt; 개념에 대해서 한 마디로 설명하자면,&amp;nbsp; 시스템 구조에 맞게 &lt;b&gt;팀을 설계하는 방법&lt;/b&gt;을 의미한다. 대표적인 팀 유형은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-section-id=&quot;1d9j86z&quot; data-end=&quot;2524&quot; data-start=&quot;2497&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Stream-aligned team&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;2539&quot; data-start=&quot;2526&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;제품 기능을 담당하는 팀&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2542&quot; data-start=&quot;2541&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2557&quot; data-start=&quot;2544&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;cmbdfw&quot; data-end=&quot;2550&quot; data-start=&quot;2544&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주문 팀&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;1x8r678&quot; data-end=&quot;2557&quot; data-start=&quot;2551&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결제 팀&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-section-id=&quot;muw2gq&quot; data-end=&quot;2580&quot; data-start=&quot;2559&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Platform team&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;2611&quot; data-start=&quot;2582&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다른 팀들이 사용하는 &lt;b&gt;공통 플랫폼&lt;/b&gt;을 만드는 팀&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2614&quot; data-start=&quot;2613&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2640&quot; data-start=&quot;2616&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;16wjjgq&quot; data-end=&quot;2623&quot; data-start=&quot;2616&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CI/CD&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;2jp0dk&quot; data-end=&quot;2629&quot; data-start=&quot;2624&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인프라&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;j5glav&quot; data-end=&quot;2640&quot; data-start=&quot;2630&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라우드 플랫폼&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-section-id=&quot;9obwm8&quot; data-end=&quot;2663&quot; data-start=&quot;2642&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Enabling team&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;2672&quot; data-start=&quot;2665&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기술 지원 팀&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2690&quot; data-start=&quot;2674&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새 기술 도입을 도와주는 역할&lt;/span&gt;&lt;/p&gt;
&lt;h1 data-section-id=&quot;1ttgibo&quot; data-end=&quot;2717&quot; data-start=&quot;2697&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;1ttgibo&quot; data-end=&quot;2717&quot; data-start=&quot;2697&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;1ttgibo&quot; data-end=&quot;2717&quot; data-start=&quot;2697&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;8. 조직과 아키텍처를 함께 설계&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;2740&quot; data-start=&quot;2719&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;2777&quot; data-start=&quot;2742&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2777&quot; data-start=&quot;2744&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;소프트웨어 아키텍처와 조직 구조는 함께 설계해야 한다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;2783&quot; data-start=&quot;2779&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜냐하면 앞서 설명해왔듯 시스템 구조, 팀 구조, 개발 프로세스가 서로 영향을 주기 때문이다. 예를 들어, 마이크로서비스를 도입했는데 팀이 하나뿐이라면 실제로는 효과가 없다. 그래서 다음과 같은 흐름으로 설계해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비즈니스 구조&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인 설계&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 구조&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;darr;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;팀 구조&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2942&quot; data-start=&quot;2926&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;es1wrp&quot; data-end=&quot;2970&quot; data-start=&quot;2949&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;es1wrp&quot; data-end=&quot;2970&quot; data-start=&quot;2949&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;es1wrp&quot; data-end=&quot;2970&quot; data-start=&quot;2949&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;9. 아키텍처는 &amp;ldquo;의지&amp;rdquo;를 구현한다&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;2985&quot; data-start=&quot;2972&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2997&quot; data-start=&quot;2987&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여기서 내가 정의하는 &lt;b&gt;의지(意志)는 &lt;/b&gt;회사나 팀이 이루고 싶은 목표이다.&amp;nbsp; 예를 들어 조직이 다음과 같은 목표를 가지고 있다면, 이 목표가 아키텍처에 반영되어야한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3057&quot; data-start=&quot;3022&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-section-id=&quot;vhhszp&quot; data-end=&quot;3033&quot; data-start=&quot;3022&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빠르게 기능 출시&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;jide7k&quot; data-end=&quot;3044&quot; data-start=&quot;3034&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;안정적인 서비스&lt;/span&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;zige85&quot; data-end=&quot;3057&quot; data-start=&quot;3045&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;확장 가능한 시스템&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;3103&quot; data-start=&quot;3091&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;목표&amp;rarr; 빠른 개발&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설계&amp;rarr; 작은 서비스 + 독립 팀&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;목표&amp;rarr; 높은 안정성&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;설계&amp;rarr; 강한 테스트 + 안정적 인프라&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3168&quot; data-start=&quot;3167&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;아키텍처는 기술 선택이 아니라 &lt;/span&gt;&lt;b&gt;조직의 전략을 구현하는 구조이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3168&quot; data-start=&quot;3167&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;ppuml0&quot; data-end=&quot;3245&quot; data-start=&quot;3225&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;ppuml0&quot; data-end=&quot;3245&quot; data-start=&quot;3225&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;10. 레거시를 경쟁력으로 바꾸기&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;3277&quot; data-start=&quot;3247&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;레거시 시스템을 단순히 제거하는 것이 아니라 &lt;b&gt;경쟁력으로 바꾸는 것&lt;/b&gt;이 목표이다. 방법은 지금까지 설명해왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3348&quot; data-start=&quot;3317&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 비즈니스 흐름 이해 (Event Storming)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3366&quot; data-start=&quot;3350&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 도메인 설계 (DDD)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3397&quot; data-start=&quot;3368&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 팀 구조 설계 (Team Topologies)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3413&quot; data-start=&quot;3399&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 시스템 구조 재설계&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3451&quot; data-start=&quot;3415&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 &lt;b&gt;기술 + 조직 + 비즈니스&lt;/b&gt;를 함께 바꾸어야 레거시 프로그램이 경쟁력을 갖추게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3451&quot; data-start=&quot;3415&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-section-id=&quot;okswgo&quot; data-end=&quot;3466&quot; data-start=&quot;3458&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;okswgo&quot; data-end=&quot;3466&quot; data-start=&quot;3458&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-section-id=&quot;okswgo&quot; data-end=&quot;3466&quot; data-start=&quot;3458&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;11. 마무리&lt;/b&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-end=&quot;3512&quot; data-start=&quot;3480&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1.&amp;nbsp; AI 시대에는 &lt;b&gt;아키텍처 설계가 더 중요해진다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3553&quot; data-start=&quot;3514&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2.&amp;nbsp; 아키텍처 모더나이제이션은 &lt;b&gt;기술 교체 프로젝트가 아니다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3593&quot; data-start=&quot;3555&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 시스템과 조직은 함께 설계해야 한다(소시오테크니컬 관점)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3669&quot; data-start=&quot;3595&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. Event Storming / DDD / Team Topologies 같은 방법을 사용해 비즈니스와 시스템을 연결해야 한다&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3683&quot; data-start=&quot;3671&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;5. 결국 아키텍처는 &lt;b&gt;조직의 의지와 전략을 구현하는 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://speakerdeck.com/nwiizo/yi-zhi-woshi-zhuang-suruakitekutiyamodanaizesiyon&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://speakerdeck.com/nwiizo/yi-zhi-woshi-zhuang-suruakitekutiyamodanaizesiyon&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/457</guid>
      <comments>https://engineer-mole.tistory.com/457#entry457comment</comments>
      <pubDate>Tue, 10 Mar 2026 21:47:22 +0900</pubDate>
    </item>
    <item>
      <title>의존성 주입 (DI: Dependency Injection)</title>
      <link>https://engineer-mole.tistory.com/456</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DI의 탄생 경위&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;참고로 코드는 Kotlin으로 작성하도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;제일 먼저 DI를 하지 않고 어느 오브젝트 내에서 외부 오브젝트를 인스턴스화하는 경우를 살펴보도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773057562678&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Apple(val hasPoison: Boolean = false)

// 식품 클래스. 추상적인 개념이어야하지만 사과(Apple)에 의존해버리고 만다.
class Food {
    fun eat() {
        val apple = Apple(hasPoison = false)
        if (apple.hasPoison) {
            println(&quot;게임 오버&quot;)
        } else {
            println(&quot;사과 맛있다！&quot;)
        }
    }
}

class Human {
    // 이 메소드에서 eat()메소드를 호출. 테스크 코드에 해당하는 부분이기도 하다.
    fun doSomething() {
        val food = Food()
        food.eat()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;doSomething() 실행 결과는 항상&lt;/span&gt; 동일하다. 위 코드는 Food클래스의&amp;nbsp;eat()메소드로, 외부의&amp;nbsp;Apple클래스를 인스턴스화하고 있다. 즉, &amp;nbsp;Food클래스의&amp;nbsp;eat()메소드는&amp;nbsp;Apple클래스에&amp;nbsp;&lt;b&gt;의존하고 있다. &lt;/b&gt;하지만 이것은 문제이다. 왜냐하면 eat()메소드의 처리 내용을 직접 편집하거나&amp;nbsp;Apple클래스를 편집하지 않는 한 eat() 메소드의 처리 결과는 변화하지 않기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 식품(Food)은 사과 이외에 바나나나 치즈도 있을 것이며, 한층 더 사과 안에도 독들이와 독 없음 2종류 있을 것인데, 식품(Food)를 먹을 때의 결과는 항상 같고, 유닛 테스트의 작성도 어렵다. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;예를 들어, eat() 메소드내에서 if문에 문제가 없는지 등을 확인하기 위해서는 , Apple 클래스를 다시 작성할 수 밖에 없다. 그로인해 테스트 코드(doSomething() 메소드)만으로 테스트하는 것은 불가능하게 되어 버린다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;nbsp;이러한 문제를 해결하기 위해 &amp;nbsp;eat() 메소드의 Apple 클래스에 대한&amp;nbsp;&lt;b&gt;의존성&lt;/b&gt;&amp;nbsp;을 일단 약하게 하고 나중에 주입하자라는 생각으로 이어지게 되었고, 이러한 경위로 의존성 주입이 탄생하게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;b&gt;&amp;nbsp;즉 하나의 객체 A가 객체 B에 의존하는 경우, 즉 객체 A가 다른 객체 B를 가지는 것은 사용할 때의 유연성이나 테스트의 어려움의 관점에서 볼 때 좋지 않으므로, 먼저 오브젝트 A가 오브젝트 B를 가지지 않도록 하고 외부에서 오브젝트 A에 오브젝트 B를 주입해 주는 것이 DI의 아이디어이다.&lt;/b&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;DI의 간단한 예에 대해서 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773058895441&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Food클래스・eat()메소드의 Apple에 대한 의존성을 없애고, Food클래스・eat()메소드를 추상화
interface Food {
    fun eat()
}

// Food 인터페이스를 비준하여, eat()메소드를 Apple클래스용으로 구현한다.
class Apple(val hasPoison: Boolean) : Food {
    override fun eat() {
        if (hasPoison) {
            println(&quot;게임 오버&quot;)
        } else {
            println(&quot;사과 맛있다!&quot;)
        }
    }
}

class Human {
    // 이 메소드로 부터 eat()메소드를 호출. 테스트 코드에 해당하는 부분이기도 하다.
    fun doSomething() {
        val apple = Apple(hasPoison = false)
        val poisonApple = Apple(hasPoison = true)
        apple.eat()
        poisonApple.eat()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; doSomething() 실행 결과는 다음과 같이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773058962920&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;사과 맛있다！ // apple.eat()의 실행결과
게임오버    　　　// poisonApple.eat()의 실행결과&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 덧붙여서, 식품(Food) 인터페이스는 사과(Apple)에 의존하지 않는 추상적인 개념이므로, 다음과 같이 바나나(Banana)도 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773058991493&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Banana : Food {
    override fun eat() {
        println(&quot;바나나를 먹으면 힘이나&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; Banana().eat() 실행 결과는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773059012173&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;바나나를 먹으면 힘이나&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DI의 종류&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;198:1-206:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;198:1-200:137&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Interface Injection&lt;/b&gt;&amp;nbsp;: 바로 앞에서 살펴본 예가 여기에 해당한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 방법에서는 인터페이스, 즉 기초가 되는 메소드군을 정의해, 작성하는 클래스에 그 인터페이스를 비준시키는 것으로 DI를 구현한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java / Kotlin에서는 인터페이스, Objective-C / Swift는 프로토콜이 인터페이스에 해당한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;201:1-202:176&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Constructor Injection&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 클래스 A의 생성자로서 그 변수 hoge 를 인스턴스화하는 것으로, 클래스 A에 Hoge 클래스의 「의존성」을 「주입」한다&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;203:1-206:0&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Setter Injection&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;한 클래스 내의 프로퍼티에 다른 클래스의 인스턴스가 되는 것을 선언만 해 두어(var hoge: Hoge?)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부로부터 액세스 가능한 setter 메소드 경유로 hoge 를 인스턴스화하는 방법으로, Hoge 클래스의 「의존성」을 「주입」한다&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&quot;의존성&quot;의 의미&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans 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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;의존성 주입에서의 &quot;의존성 = 객체 &quot;. 영어판 Wikipedia에는 의존성을 객체라고 정의하고 있다. 즉, &quot;의존성=인스턴스&quot;,&amp;nbsp; &quot;의존성=Java / Kotlin 등이라면&amp;nbsp;interface에서 정의 된 메소드의 처리 내용을 실제로 기술하고있는 메소드&quot;, &quot;의존성 =Objective-C/Swift에서 말하는 것을&amp;nbsp;protocol비준하고 있는 클래스내에서 처리 내용을 실제로 기술하고 있는 델리게이트 메소드&quot;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans 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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&quot;의존성 주입&quot;이란&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-sourcepos=&quot;268:1-268:212&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;(오브젝트 A의 외부로부터) 「오브젝트 A에서 필요한, 오브젝트 B의 객체(인스턴스나 구체적인 메소드)」를 「주입/대입/설정/제공」하는 것이다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;270:1-271:84&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라고 말을 더해 설명할 수 있겠다. 덧붙여 여기서 객체는 클래스나 메소드에 해당한다. 하지만 이것만으로는, 아직 뭐야라고 생각할지도 모른다. 앞에서 설명한 3가지 DI 방법을 의존성 정의에 따라 다음과 같이 따라 바꿔 보았다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;278:1-285:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;278:1-279:150&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Interface Injection&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메소드 A가 정의된 인터페이스를 비준한 클래스 B로, 메소드 A의 구체적인 처리를 구현하는 것이다&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;280:1-281:130&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Constructor Injection&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 A의 생성자로 클래스 B를 설정하고 클래스 A 생성시 클래스 B를 구현하는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;282:1-285:0&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Setter Injection&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저 클래스 A의 속성으로 클래스 B를 설정하고 클래스 B의 setter 메서드도 정의한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 클래스 A 생성 후 클래스 B의 setter 메서드를 호출하여 클래스 B를 구현하는 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1773059574966&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Food {
    fun eat()  // &amp;lt;- 메소드A
}

// Apple &amp;lt;- 클래스B
class Apple(val hasPoison: Boolean) : Food {
    // 여기서「메소드A의 구체적인 처리를 구현」
    override fun eat() {
        if (hasPoison) {
            println(&quot;게임 오버&quot;)
        } else {
            println(&quot;사과 맛있다!&quot;)
        }
    }
}


class Human {
    // 이 메소드에서 eat()메소드를 호출. 테스트 코드에 해당하는 부분도 있다.
    fun doSomething() {
        val apple = Apple(hasPoison = false)
        val poisonApple = Apple(hasPoison = true)
        apple.eat()
        poisonApple.eat()
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DI의 장점&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;마지막으로 지금까지 중간 중간에 설명했던 DI의 장점을 모두 정리하도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;325:3-327:55&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;325:3-325:64&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결합 정도의 저하에 의한 컴포넌트화 촉진&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;326:3-326:31&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단위 테스트 효율성&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;327:3-327:55&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특정 프레임워크에 대한 의존도 감소&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://qiita.com/iTakahiro/items/353a11f6c9d2a927158d&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://qiita.com/iTakahiro/items/353a11f6c9d2a927158d&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/456</guid>
      <comments>https://engineer-mole.tistory.com/456#entry456comment</comments>
      <pubDate>Mon, 9 Mar 2026 21:34:15 +0900</pubDate>
    </item>
    <item>
      <title>VSCode(Visual Studio Code)의 GitHub Copilot SKILL.md</title>
      <link>https://engineer-mole.tistory.com/455</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;시작하기 전에&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; copilot-instructions.md 하나에 많은 정보를 제공할 수 있지만. 그 때문에 추론에 사용하는 모델의 성능이 낮거나, 한 번의 세션에서 대화가 오래 지속되면 처음에 말했던 내용이나, 애초에 copilot-instructions.md에 정의하고 있던 것을 잊어버리는 경우 생긴다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한, 제공한 많은 맥락이 잡음이 되어 추론에 시간이 오래 걸리거나 엉뚱한 답을 내는 경우도 있었습니다. 그런 문제를 보완하기 위해 SKILL.md를 활용할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Agent Skills란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;에이전트 스킬은 Copilot이 관련된 경우에 불러올 수 있는 명령, 스크립트 및 리소스 폴더이며, 특수 작업의 성능을 향상시킵니다. 에이전트 스킬은 다양한 에이전트에서 사용되는 표준이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;nbsp;스킬이라는 이름만 듣자면 어떤 도구를 사용하는 능력이나 도구 자체를 정의하는 것처럼 들리지만, 실제로는 프로젝트의 도메인 지식 등을 넣는 것이 가장 일반적인 사용 방법이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;실제로 어떤 식으로 사용되는지 소개하도록 하겠다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;아래가 SKILL.md 중 하나이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772019323135&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
name: testing
설명: TooMe's Task App (Web)의 테스트 전략. Vitest, React Testing Library, MSW를 사용한 단위&amp;middot;컴포넌트 테스트에 대해 설명. 테스트를 만들 때 참고.
---

# 테스트 전략

## 기술 스택

- **단위 테스트**: Vitest
- **컴포넌트 테스트**: React Testing Library + user-event
- **API Mock**: MSW (Mock Service Worker)
- **E2E테스트**: Playwright / Cypress
- **훅 테스트**: `@testing-library/react-hooks` (또는 RTL의 `renderHook`)

## 유닛 테스트 (Utils / Hooks / Stores)

- 순수 함수 utils, 커스텀 훅,USTStand 스토어 로직을 검증한다
- 비동기 처리를 포함하는 경우에는 `대기`와 `act`를 적절히 사용한다

```typescript
// stores/authStore.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useAuthStore } from './authStore';

describe('useAuthStore', () =&amp;gt; {
beforeEach(() =&amp;gt; {
const { result } = renderHook(() =&amp;gt; useAuthStore());
act(() =&amp;gt; result.current.logout());
});

it('should set user and authentication status', () =&amp;gt; {
const { result } = renderHook(() =&amp;gt; useAuthStore());
const mockUser = { uid: '123', email: 'test@example.com' };

act(() =&amp;gt; {
result.current.setUser(mockUser as any);
});

expect(result.current.user).toEqual(mockUser);
expect(result.current.isAuthenticated).toBe(true);
});
});
```

## 컴포넌트 테스트 (UI / 통합)

- 사용자 인터랙션(클릭, 입력)을 `user-event`로 시뮬레이션한다
- 구현 상세가 아니라 사용자가 볼 수 있는 행동(접근성 속성이나 텍스트)을 테스트한다

```typescript
// features/task/components/TaskInput.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TaskInput } from './TaskInput';
import { vi } from 'vitest';

describe('TaskInput', () =&amp;gt; {
it('calls onSubmit with input text when enter is pressed', async () =&amp;gt; {
const handleCreate = vi.fn();
const user = userEvent.setup();

render(&amp;lt;TaskInput onCreate={handleCreate} /&amp;gt;);

const input = screen.getByRole('textbox', { name: /タスクを入力/i });

// 입력 및 전송 시뮬레이션
await user.type(input, 'New Task{enter}');

expect(handleCreate).toHaveBeenCalledWith('New Task');
expect(input). &amp;forall; 값(''); // 전송 후 클리어 확인
});
});
```

## API 목업 (MSW)

- 컴포넌트 테스트나 통합 테스트에서 API 응답이 필요할 경우 MSW를 사용한다
- 실제 네트워크 요청이 발생하지 않도록 한다

```typescript
// tests/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
http.get('/api/tasks', () =&amp;gt; {
return HttpResponse.json([
{ id: '1', title: 'Buy milk', completed: false },
{ id: '2', title: 'Walk the dog', completed: true },
]);
}),
];
```

## E2E 테스트

- 비판적인 사용자 여정(로그인 &amp;rarr; 작업 생성 &amp;rarr; 완료)를 검증한다
Playwright 또는 Cypress를 사용해 실제 브라우저 환경에서 동작을 확인한다

## 테스트 베스트 프랙티스

- **AAA 패턴**: Arrange (준비), Act (실행), Assert (검증) 을 의식한다
- **쿼리 우선순위**: `getByRole` &amp;gt; `getByLabelText` &amp;gt; `getByText` &amp;gt; `getByTestId` 순서대로 요소를 가져온다
- **비동기 대기**: 데이터를 가져오는 대기 등에는 `await screenfindBy...` 또는 `대기` 를 사용한다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;md 파일의 시작 부분에는 특징적인 파티션이 있으며, 그곳에 name과 description이 설정되어 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;간단히 이 파라미터들에 대해 설명하도록 하겠다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파라미터명&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;name&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; SKILL.md 가 있는 디렉터리 이름. 디렉터리 구조에 대해서는 뒤에서 설명하도록 하겠다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;description&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 스킬의 기능과 사용 시점을 설명한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; GitHub Copilot은 각 SKILL.md를 필요에 따라 불러와 작업을 실행한다. 필요에 따라 설명을 디스크립션으로 정의하는 이미지를 떠올리면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;앞서 예시에서는 다음과 같이 description을 정의했다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;TooMe's Task App (Web)의 테스트 전략. Vitest, React Testing Library, MSW를 사용한 단위&amp;middot;컴포넌트 테스트에 대해 설명. 테스트를 만들 때 참고.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 테스트 관련 작업을 실행할 때 이 SKILL.md읽게 된다. 이를 통해 주요 copilot-instructions.md 테스트 전략과 관련된 컨텍스트를 분리된다. 더욱이, 이 분리된 컨텍스트는 필요에 따라만 읽히게 된다.&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;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;158:1-158:37&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;SKILL.md의 작성법, 사용법&lt;/b&gt;&lt;/span&gt;&lt;/h2&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-sourcepos=&quot;159:1-159:64&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. vsode의 설정에서 chat.useAgentSkills을 유효화&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-sourcepos=&quot;159:1-159:64&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;설정 단축키를 누른 후, chat.userAgentSkills를 입력한 후 유효화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 2. 다음과 같은 디렉터리 구조로 각 SKILL.md를 정의&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 하면 GitHub Copilot이 필요에 따라 필요한 SKILL.md 참고하게 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;물론 여기서 정의한 디렉터리는 예시일 뿐이므로, 프로젝트에 따라 원하는 만큼 새 디렉터리를 만들고 그 안에 SKILL.md를 추가해도 괜찮다. 즉, 아래와 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;같은 구성을 하지 않아도, vscode의 chat.agentSkillsLocations를 설정하면 원하는 디렉터리 구성을 만들 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772019662089&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.github
│── skills
│   ├── architecture
│   │   └── SKILL.md
│   ├── ui-design
│   │   └── SKILL.md
│   ├── state-managment
│   │   └── SKILL.md
│   ├── backend-integration
│   │   └── SKILL.md
│   ├── coding-standards
│   │   └── SKILL.md
│   └── testing
│       └── SKILL.md
└── copilot-instructions.md&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 Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;이로 인해 copilot-instructins.md도 매우 깔끔해진다. 다음과 같이 말이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1772019753454&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Copilot Instructions for TooMe's Task App (Web)

## 전제 조건

- **답변은 반드시 한국어로 해 주세요. **
- 코드를 변경할 때, 변경량이 200줄을 초과할 가능성이 높다면, 사전에 &amp;ldquo;이 지시에서는 변경량이 200줄을 초과할 가능성이 있습니다만, 실행하시겠습니까?&amp;rdquo;라고 사용자에게 확인하도록 해 주세요.
- 큰 변화를 주고자 할 경우, 먼저 무엇을 할지 계획을 세운 뒤, 사용자에게 &amp;ldquo;이와 같은 계획으로 진행하려고 합니다.&amp;rdquo;라고 제안해 주세요.

## 참고 스킬 가이드 (Skills)

특정 작업을 실행할 때는 반드시 아래의 해당 문서를 참고하고, 그 지침을 따르세요.

- **아키텍처 디렉터리 구성**
- 파일 배치, 모듈 구성, 기능 분리
-   `.github/skills/architecture/SKILL.md`

- **UI 구현&amp;middot;스타일링**
- Tailwind CSS, 컴포넌트 설계, 접근성 (a11y)
-   `.github/skills/ui-design/SKILL.md`

#### 생략&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/span&gt;&lt;/h2&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;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; GitHub Copilot의 최신 기능인 Agent Skills를 활용하고, SKILL.md를이용해 이전보다 더욱 강력하게 GitHub Copilot을 활용하는 방법을 소개했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러분도 꼭 자신의 프로젝트에 Agent Skills를 도입해, &amp;lsquo;그 정도로 뛰어난 어시스턴트&amp;rsquo;를 &amp;lsquo;믿을 수 있는 에이전트&amp;rsquo;로 키워 보길 바란다. &lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://qiita.com/TooMe/items/230a730ce0387c77e822&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://qiita.com/TooMe/items/230a730ce0387c77e822&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/코딩툴</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/455</guid>
      <comments>https://engineer-mole.tistory.com/455#entry455comment</comments>
      <pubDate>Wed, 25 Feb 2026 20:45:46 +0900</pubDate>
    </item>
    <item>
      <title>MyNavi 전직 페어 (MyNavi転職フェア)  참가 후기</title>
      <link>https://engineer-mole.tistory.com/454</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;작년 말에 최근 일본 엔지니어 이직 동향이나 분위기 파악을 위해 MyNavi 전직 페어를 참가했었기에 관련 후기를 짧막하게 공유하고자한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;MyNavi 전직 페어는 다양한 지역에서 동시에 개최되고 있고 경우에 따라 엔지니어 메인이라던가 테마가 정해지는 경우가 있다. 전직 페어 개최 장소랑 참가 기업은 아래의 MyNavi 전직 페어 사이트에서 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769943005109&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;転職フェア・イベントならマイナビ転職～日本最大級の転職セミナー | 転職サイトは【マイナ&quot; data-og-description=&quot;マイナビ転職の転職フェア・イベント(合同企業説明会)は全国各地で開催！　企業との出合いはもちろん、キャリアアドバイザーとの面談など転職支援サービスが充実、あなたの転職活動を&quot; data-og-host=&quot;tenshoku.mynavi.jp&quot; data-og-source-url=&quot;https://tenshoku.mynavi.jp/event/&quot; data-og-url=&quot;https://tenshoku.mynavi.jp/event/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CFwGU/dJMb86nSytb/EmGeuPYc4nZ7QdwGecMvG0/img.jpg?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/bPb3EW/dJMb83SdQk8/DYmkvJpcHoR8IKwJpRwGB0/img.png?width=950&amp;amp;height=330&amp;amp;face=0_0_950_330,https://scrap.kakaocdn.net/dn/bASbug/dJMb86OWPyK/hku91W3pXTkQsAbucBtZCk/img.png?width=206&amp;amp;height=334&amp;amp;face=0_0_206_334&quot;&gt;&lt;a href=&quot;https://tenshoku.mynavi.jp/event/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tenshoku.mynavi.jp/event/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CFwGU/dJMb86nSytb/EmGeuPYc4nZ7QdwGecMvG0/img.jpg?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/bPb3EW/dJMb83SdQk8/DYmkvJpcHoR8IKwJpRwGB0/img.png?width=950&amp;amp;height=330&amp;amp;face=0_0_950_330,https://scrap.kakaocdn.net/dn/bASbug/dJMb86OWPyK/hku91W3pXTkQsAbucBtZCk/img.png?width=206&amp;amp;height=334&amp;amp;face=0_0_206_334');&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;転職フェア・イベントならマイナビ転職～日本最大級の転職セミナー | 転職サイトは【マイナ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;マイナビ転職の転職フェア・イベント(合同企業説明会)は全国各地で開催！　企業との出合いはもちろん、キャリアアドバイザーとの面談など転職支援サービスが充実、あなたの転職活動を&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tenshoku.mynavi.jp&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;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;내가 참가했던 전직 페어는 작년인 25년 11월 29일 토요일 신주쿠에서 엔지니어 전직 응원 테마로 열린 행사였다. 일단 참가하기 위해 MyNavi 전직 어플리케이션을 다운 받아야할 필요가 있다. 어플리케이션 다운로드 후 회원가입은 물론 이력서와 같이 이직에 필요한 정보를 입력해야한다.&amp;nbsp; 그 후엔 お役立ち탭 &amp;gt; 개최 목록에서 참가하고 싶은 행사를 선택, 사전 신청 혹은 캔슬 버튼을 누르면 끝이다.&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;KakaoTalk_20260201_202619023.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdTPtp/dJMcai9XCUV/X9qH9d2y1rAj2FHADhsJG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdTPtp/dJMcai9XCUV/X9qH9d2y1rAj2FHADhsJG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdTPtp/dJMcai9XCUV/X9qH9d2y1rAj2FHADhsJG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdTPtp%2FdJMcai9XCUV%2FX9qH9d2y1rAj2FHADhsJG0%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;1179&quot; height=&quot;2556&quot; data-filename=&quot;KakaoTalk_20260201_202619023.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&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 Sans Light';&quot;&gt;&amp;nbsp;행사 당일 장소에 가면, 먼저 이력서를 출력해야한다. 어플리케이션을 기동한 다음 신청시에 들어갔었던 お役立ち탭에 들어간 후 QR코드 표시 버튼을 누르면 QR코드가 표시되는데, 컴퓨터와 프린터기가 나열되어 있는 곳으로 가서 이 QR코드를 스캔하여 이력서를 출력하면 된다. 출력 후 입장 전 설명을 위해 잠시 대기하게 되고 설명이 끝나면 본격적으로 입장을 하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;Type 전직 페어와 다른 점이라고 하면 입장하면 MyNavi전직 페어 직원이 1:1로 붙어 이벤트 개요를 설명해주고, 기업 부스로 안내를 해준다는 점이다. 어떤 이직 기준으로 기업을 찾고 있는지 질문을 해오는데 그 질문에 대해 대답을하면 참가한 기업 중 관련 기업 목록을 보여주고, 상담하고 싶은 기업이 있다고하면 그 기업 부스로 안내해주는 형식이다. 기업 부스로 안내된 뒤 상담이 끝나면 다시 찾아와달라고 했는데, 나는 굳이 다시 찾진 않았다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&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 Sans Light';&quot;&gt;&amp;nbsp;나는 당시 한 열군데 정도의 부스에 들렸었다. 그 중 관심이 생긴 기업은 세 군데였고, 하고 싶은 일이나 기업 분위기에 맞을 거 같다고 느낀 딱 한 곳 마이나비 전직 홈페이지를 통해 지원을 했었다. 결과는 서류 불합격이라 나의 착각이었구나 했지만,&amp;nbsp; 현재 일본 채용 분위기를 느낄 수 있었다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&amp;nbsp;내가 당시 느낀 최근 일본 채용 분위기는 금융 프로젝트관련된 엔지니어 혹은 인프라 엔지니어를 많이 원하는 느낌이었다. 금융 기업(은행, 증권사) 프로젝트 엔지니어를 구인하는 기업이 굉장히 많았고, 프로그래밍 보다는 기반을 구축하는 인프라 엔지니어 포지션이 많이 열려 있었다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;뉴스나 각 종 매체를 통해 현재 일본은 투자, 주식관련된 관심이 한국보다는 아니지만 조금씩 증가하고 있어 이런 고객을 잡기 위해 새로운 어플리케이션이나 기반 만들려고 하는 움직임이나 개선을 시행하고 있다고 봤는데 이 움직임이 일본 엔지니어 채용 세계에 큰 영향을 주고 있구나를 체감하게 됐다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>개인 기록/도전과 성장</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/454</guid>
      <comments>https://engineer-mole.tistory.com/454#entry454comment</comments>
      <pubDate>Sun, 1 Feb 2026 20:25:11 +0900</pubDate>
    </item>
    <item>
      <title>일본 재류 자격 갱신하기</title>
      <link>https://engineer-mole.tistory.com/453</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;얼마전 재류 자격 갱신을 했으므로 관련 기록을 남기고자 한다.&amp;nbsp; 원래는 기록으로 남길 생각이 없었지만 조금 특이 케이스인것 같아 비슷한 상황에 놓인 분들을 위해 경험담을 공유하고 싶어 작성한다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;재류 자격 갱신에 필요한 서류는 재류 자격과 회사 규모(&quot;카테고리&quot;라는 용어로 구분)에 따라 다르다. 각각의 필요 서류는 &lt;/span&gt;&lt;a href=&quot;https://www.moj.go.jp/isa/applications/procedures/16-3.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;출입국재류 관리청 사이트&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;에서 확인할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;현재 기술, 인문지식, 국제 업무 재류 자격으로 일본에 있으며 근무중인 회사는 카테고리2에 속하는 회사이다.&amp;nbsp; &amp;nbsp;이 경우 기본적으로 필요한 서류는 다음과 같다.&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 Sans Light;&quot;&gt;재류자격갱신허가신청서 1통 &amp;rarr; 본인준비+회사준비&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.moj.go.jp/isa/applications/status/photo_info_00002.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;사진 1장&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;(3x4 사이즈) &amp;rarr; 본인준비 &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-origin-width=&quot;250&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCtEWk/dJMcabJNY4J/f3GYK4JKQXzSnI5NSvUkK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCtEWk/dJMcabJNY4J/f3GYK4JKQXzSnI5NSvUkK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCtEWk/dJMcabJNY4J/f3GYK4JKQXzSnI5NSvUkK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cCtEWk/dJMcabJNY4J/f3GYK4JKQXzSnI5NSvUkK0/img.gif&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;250&quot; height=&quot;246&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&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 Sans Light;&quot;&gt;여권 혹은 재류카드 제시 &amp;rarr; 본인준비 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;전년도 직원의 급여소득원청징수표 등의 법정조서 합계표(사본) &amp;rarr; 회사준비&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;그러나 이번 갱신의 경우 추가로 &quot;활동기관에서 이탈과 이관의 신청&quot;이라는 서류를 하나 더 준비할 필요가 있었다. 이유는 내가 속한 부서가 그룹 회사의 다른 회사로 이관됐고, 새로 근로 계약을 채결했기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;즉 이 경우는 &lt;/span&gt;&lt;a href=&quot;https://www.moj.go.jp/isa/applications/procedures/shozokunikansuru_00001.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;출입국재류관리청 Q&amp;amp;A&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt; Q23번에 제시되어 있는 자료의 4번 흡수분할, 사업양도에 해당하는 케이스고, 원래라면 노동계약이 상속되지만 별도로 새로 근로 계약을 채결했기에 ※2의 설명보충부분에 적혀있듯 &quot;계약 종료 및 새로운 계약 체결&quot; 서류를 추가로 제출해야했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;참고로 이 서류는 재류 기간 중 이직을 한 경우에도 제출해야한다. 원칙상 이러한 소속 기관 변경 서류는 14일 이내에 제출해야하지만, 지난 재류카드 갱신도 그렇고 이번 재류 카드 갱신시에도 14일이 훌쩍 지나 재류 카드 갱신기간에 제출했지만 큰 불이익은 없었다. 하지만 앞으로 외국인 관련 비자 심사가 까다로워질 것 같기에 혹시라도 이직을 하거나 나와 같은 케이스가 발생했다면 14일이내에 제출하도록 하자. 온라인으로 신청이 가능한 걸로 알고 있다.&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-origin-width=&quot;958&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lvZfP/dJMcacPsMQB/iIq31QDU1MF40Rudk6MRY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lvZfP/dJMcacPsMQB/iIq31QDU1MF40Rudk6MRY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lvZfP/dJMcacPsMQB/iIq31QDU1MF40Rudk6MRY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlvZfP%2FdJMcacPsMQB%2FiIq31QDU1MF40Rudk6MRY1%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;958&quot; height=&quot;723&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;723&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 Sans Light;&quot;&gt;참고로 각 케이스에 따른 제출 서류 다운로드는 &lt;/span&gt;&lt;a href=&quot;https://www.moj.go.jp/isa/applications/procedures/nyuukokukanri10_00014.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;소속 기관에 관련된 신청&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;에서 확인할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;관련된 서류를 모두 챙겨서 나는 거주하고 있는 지역에서 가장 &lt;/span&gt;&lt;a href=&quot;https://share.google/MXcFUNjmeSsr1Cljk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;도쿄출입국재류관리국 마쓰도 출장소&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;로 향했다. 참고로 여기는 모든 구의 주민이 신청할 수 있는 곳은 아니다. 주소지가 다음과 같이 치바현, 이바리키현, 도쿄-아라카와구, 도쿄-아다치구, 도쿄-가쓰시카구, 도쿄-에도가와구인 사람만 신청가능하다. 관련 내용은 &lt;/span&gt;&lt;a href=&quot;https://www.moj.go.jp/isa/about/region/tokyo/16_00405.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;여기서&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Sans Light;&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-origin-width=&quot;587&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F8ZAm/dJMcabXjimI/7dSGB8q5eSZOgFRl0pZzjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F8ZAm/dJMcabXjimI/7dSGB8q5eSZOgFRl0pZzjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F8ZAm/dJMcabXjimI/7dSGB8q5eSZOgFRl0pZzjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF8ZAm%2FdJMcabXjimI%2F7dSGB8q5eSZOgFRl0pZzjK%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;587&quot; height=&quot;310&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;310&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 Sans Light;&quot;&gt;&amp;nbsp;마쓰도 출장소의 경우 키테미테 마츠도라는 쇼핑센터의 8층에 위치한다. 참고로 조그만한 팁이 있다면 키테미테 마츠도 정면의 광장쪽 엘레베이터는 아침 9시에 열린다. 하지만 뒷편, 즉 주차장쪽에 가까운 엘리베이터는 9시전에도 가동하기 때문에 이쪽 엘리베이터를 이용해 9시가 되기전에 8층으로 올라간 후 출입국관리소에 줄을 서면 정면 엘리베이터를 이용하는 다른 사람보다 빠르게 접수할 수 있다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;아무튼 출입국 관리소에 도착해 관련 서류체출+여권 혹은 재류카드를 제시하고 잠시 대기했다가 결과를 안내할 때 사용하는 엽서에 주소를 기재하면 신청은 끝난다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;재류자격심사기간은 케이스 바이 케이스이겠지만 내가 신청한 12월중순엔 거의 5일만에 심사 결과가 나왔다. 필요한 서류를 잘 준비한다면 거의 일이주안에 결과가 나오는거 같다. &amp;nbsp;갱신 결과를 확인하라는 엽서에 적힌 적힌 준비물을 챙겨서 다시 출입국 관리소로 가면 된다.&amp;nbsp;&amp;nbsp; 여담이지만 첫 번째 갱신때엔 수수료가 비싸지 않아 수입인지를 편의점에서 구입했던 기억이 있었는데, 최근 재류자격관련 수수료가 대폭 인상되수입인지 6천엔짜리가 필요했는데, 편의점에 물어보니 6천엔짜리의 경우 우체국에서만 판매한다고 해서 우체국에서 구입했다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;출입국관리소에 엽서에 적혀있던 필요한 준비물을 제시하면 새로운 재류카드를 받게 되고, 예전 재류카드의 경우 구멍을 뚫어 다시 돌려준다. 새 재류카드를 받게 되면 빠른 시일내에 해야할게 두 가지 있는데, 하나는 마이 넘버카드 기간 연장이고, 다른 하나는 본인이 계좌를 갖고 있는 모든 은행의 거래 목적확인을 받는 것이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp; 마이넘버카드 기간 연장은 본인이 살고 있는 지역에 있는 구약소 혹은 구약소 출장소에 들려서 처리하면 되고, 은행 거래 목적 확인의 경우 재류 자격 만료 기간이 가까워오면 은행에서 거래 목적 확인이라고 적힌 우편물이나 메일이 오는데 그에 적힌 내용에 따라 인터넷이나 우편으로 근무처를 포함한 개인정보를 작성한 후, 새로운 재류 카드 앞면과 뒷면 이미지를 제시하면 된다.&amp;nbsp;이로서 일본 재류자격에 관련된 주요 업무(?)는 끝나게 된다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&amp;nbsp;여기서 부턴 개인적인 이야기이지만, 첫 번째 3년, 두 번째도 3년 받았는데 이번 갱신으로 처음으로 재류 자격 5년 받았다. 첫 5년이라 일본에서 안정적으로 일하고 있는 외국인이라고 출입국관리소에게 인정받은 기분도 드는 동시에 이렇게 일본에 몇 번이고 재류 자격을 갱신할 만큼 오래 지내게 될지 몰랐는데 라는 생각도 들었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>개인 기록/도전과 성장</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/453</guid>
      <comments>https://engineer-mole.tistory.com/453#entry453comment</comments>
      <pubDate>Mon, 26 Jan 2026 21:19:38 +0900</pubDate>
    </item>
    <item>
      <title>2025년을 되돌아보며 세운 2026년 목표</title>
      <link>https://engineer-mole.tistory.com/452</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&amp;nbsp;2025년의 테마는 '유저와 관련된 다른 분야를 공부하자'였지만, 그렇게 보내지 못했다. 변명아닌 변명을하자면 ..... 소속되어 있던 서비스 팀은 규모가 작아 엔지니어 팀은 나를 포함한 두 명인 작은 팀이었는데, 유일한 팀 멤버가 육아휴가로 반년정도 쉬게 되면서 혼자 CS팀과의 소통, 개발, 유지/보수 모두를 담당하게 되면서 정신없는 반년을 보내게 됐다. 그리고 그 사이에 부서가 다른 그룹 회사로 이동이 결정되면서 관련된 대응도 했다. 반년 후 팀 멤버가 육아휴가로 부터 복직했지만 다른 팀의 개발 지원 + 육아와의 병행으로 실질적으로 함께 일을 할 수 있는 시간이 짧은 관계로 계속해서 혼자서 모든 일을 담당했다. 또한 연말엔 갑자기 내가 다른 서비스로의 이동 발령이 나면서 계속해서 정신이 없었다. 그래서 계속해서 공부를 미룬채로 결국 2025년이 끝나버렸다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&amp;nbsp;그럼에도 2025년에 대한 회고는 해야할거 같은 이상한 의무감이 있기도하고 아카이브를 남기고 싶어 글을 남긴다. 이번에도 작년 폼을 재활용해서 1년 어떤 이벤트가 있었는지 되짚어보는 시간을 가지려고 한다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&lt;b&gt;나의 2025년&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년에 새로 시작한 시도나 취미&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&amp;nbsp;새롭게 시작한 취미는 없다. 계속해서 운동으로 주 2, 3회 웨이트를 했으며 그 외에 무언가 새로운 운동을 시작하거나 하진 않았다. 생활 속에서의 시도라면 주변 친구들에 비해서 미니멀 리스트편에 속하지만 더욱 미니멀 라이프를 하고 싶어서 인터넷을 통해 물건을 주문하는 걸 줄이는 시도를 했다. 특히 나의 경우는 옷이 많았는데 안 입는 옷은 주변 지인들에게 다 나눠주고 새로운 옷을 사고 싶을 땐 인터넷으로 주문하지 않고 실제로 보고 입어보고 마음에 들면 사는 방식으로 변경했다. 덕분에 옷장의 옷과 옷에 대한 소비도 굉장히 줄었고, 현재 있는 옷들에 대한 소중한 마음도 생겼다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt; 또한 회사에선 AI 활용 팁 공유회를 개최했다. 3회 정도 개최했으며 일 혹은 일상에서 AI를 활용할 때 유용한 설정이나 프롬프트 작성법을 공유하는 방식으로 진행했다. 그리고 매 회 모아진 팁은 하나의 문서로 만들어 다른 엔지니어 팀에도 공유했다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년 가장 게을렀던 시간&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;연말인거 같다. 12월 초 다른 팀으로 갑자기 이동하게 되면서 심적부담으로 바쁘다고 생각했지만, 실질적으로 일이 바쁘진 않았던 거 같다. 그 영향으로 온갖 잡생각이 들기도 했고 겨울이 오면 항상 햇빛 부족으로 겪는 계절적 우울감으로 잠이 쏟아졌다. 그래서 매일 밤 9시나 10시즈음 잠에 들어 아침 8시 30분에 일어나는 잠만보 생활을 이어왔다.&amp;nbsp;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년 가장 몰두한 일&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;일이 아닐까? 내 의지로 그렇게 된건 아닌거 같긴한데, 어찌됐든 혼자서 모든 일을 하게 되면서 일에 몰두하게 됐다. 덕분에 다른 팀과의 교류도 늘기도 했고 반년간 혼자서 서비스를 운영했던 실적을 인정받아 부서 내에서 상을 받기도 했다. 소소한 상금은 덤.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년 나에 대해서 새로 알게 된 점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;감정적인 면이 있다는 것. 나는 감정적으로 둔하다는 생각을 했었다. 이유는 지금까지 어떤 문제는 T 90%를 발휘해서 이성적으로 생각해왔고 화를 내거나 눈물을 흘리는 등의 감정 표출은 1년에 1, 2번 있을까 말까했기 때문이다. &lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;그렇게 느낀 결정적인 사건은 올해 9월에도 어김없이 2주정도 한국에 다녀왔을 때 였다. 지금까지 부모님과 헤어질 때 눈물한 번 흘린적 없었지만 이번에 도쿄로 돌아가는 비행기에서 눈물을 흘렸다. 부모님이 아프거나와 같은 무슨 심각한 문제가 있었던건 아니고, 당시 아침 비행기로 아침을 못 먹고 갈 나를 위해 새벽부터 생과일 주스를 만들어 줬는데 예상외로 새벽부터 출국 게이트쪽 줄이 너무 길어서 그 음료수를 제대로 다 마시지 못하고 인파에 밀려 작별 인사를 할 시간도 가지지 못한 채로 그대로 헤어지게 됐다. 그 정성과 아쉬움때문에 나도 모르게 눈물이 났다.&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;근데 우는 순간에도 왜 울게 됐지 하고 원인을 동시에 생각하는스스로 정상적인 인간 맞는지에 대한 의문도 동시에 들었다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년 가장 잘한 소비&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;가습기. 이전에 쓰던 가습기가 갑작스럽게 고장나면서 이번에 큰 마음먹고 가격대가 있는 ZOJIRUSHI의스팀식 가습기를 구매했는데, 소리는 조금 시끄럽긴 하지만,&amp;nbsp; 가습 효과가 확실하고 일본의 추운 집을 커버해주면서 올 겨울은 비교적 따뜻하게 보낼 수 있었다. 참고로 구매한건 아래의 모델.&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;br&gt;&lt;a href=&quot;https://kakaku.com/item/K0001704543/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;https://kakaku.com/item/K0001704543/&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;2025년 가장 소중한 배움&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;2025년 말 &quot;지금 당장 주목을 받는 받지 못하든 온라인에서 나만의 세계관을 만들어 놓는것&quot; 이 문구를 보고 느낀점이 있다. 작년 회고에서 블로그의 역할을 지식 공유보다는 개인 경험공유와 노하우 공유로 바뀌고 있는 느낌이 들고 관련된 내용으로 블로그를 운영할 거라고 얘기했었지만 속마음 한편으로는 AI는 계속해서 발전해 나갈 것이고 나 자체의 브랜드 가치는 별로 없으므로 나에 대한 이야기를 누가 궁금해 할까라는 생각으로 블로그 운영에 대한 회의감을 계속해서 갖고 있었다. 그래서 반쯤 방치상태로 계속 뒀는데, 위 문구를 보고&amp;nbsp; 그럼에도 계속해서 블로그를 운영해 나가자는 마음을 먹게 됐다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&lt;b&gt;2026년 목표&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&amp;nbsp;블로그의 운영을 지속해서 나만의 세계관 만들기가 메인이다. 이 목표를 가진 이유는 위에서 언급했기에 생략한다. 나만의 세계관을 만들기 위한 사적인 얘기나 일본 생활에 대한 이야기를 다룰 필요가 있고 그럴 생각인데, IT지식을 메인으로 다루는 이 블로그의 성격과 완전히 다르고 회사 사람들이 이 블로그를 알고 있기도하며 개인사를 담기엔 과거 경험을 통해 t스토리보다는 네이버 블로그가 좋다는 이미지가 어렴풋있어서 아예 네이버 블로그를 새로 만들까 싶기도 하다. &lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;그리고 새로운 사람과의 교류를 늘이기를 2순위로 두려고 한다. 어릴적부터 내향적 성격으로 한정된 사람들과의 교류만 해왔는데 30대에 접어들면서 이 성격으로 인해 다양한 기회를 놓치고 있지 않지 않는가라는 생각이 들어 조금씩이라도 모임에 나가는 등 새로운 사람과의 교류를 늘리려 한다.&amp;nbsp;그 외엔 이동해 온 팀에 대한 적응 등 잔잔한 목표도 있지만 너무 잔잔한 목표들이라 여기엔 적지 않겠다!&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;마지막으로 챗지피티가 나에게 해 준 조언으로 마무리.&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;615&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UOSrq/dJMcaf6qmdC/3qJ62Ot24nJvPYxGMs03Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UOSrq/dJMcaf6qmdC/3qJ62Ot24nJvPYxGMs03Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UOSrq/dJMcaf6qmdC/3qJ62Ot24nJvPYxGMs03Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUOSrq%2FdJMcaf6qmdC%2F3qJ62Ot24nJvPYxGMs03Tk%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;835&quot; height=&quot;615&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;615&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>개인 기록/올해의 목표</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/452</guid>
      <comments>https://engineer-mole.tistory.com/452#entry452comment</comments>
      <pubDate>Wed, 14 Jan 2026 22:50:44 +0900</pubDate>
    </item>
    <item>
      <title>git rebase의 두 가지 사용법</title>
      <link>https://engineer-mole.tistory.com/451</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;※ &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;일본어 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;rebase란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr 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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 우선 rebase 명령어를 한마디로 표현하면 '지정한 커밋을 브랜치를 바꿔서 다시 만들거나 한 묶음으로 만들어서 로그를 깨끗하게 하는 명령어'이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;더 심플하게 말하면 '지정 커밋을 다시 만들어 로그를 청소하기 위한 명령어'라고도 할 수 있을 것이다. 하지만 이렇게 말하면 이해하기 어려울 것이므로. 그 사용법과 함께 설명하고자 한다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;rebase의 두 가지 중요한 쓰임새&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr 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 Sans Demilight, Noto Sans KR;&quot;&gt;제목에서 알 수 있는 rebase에는 두 가지 사용법이 존재한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 한쪽은 「각각의 브랜치로 늘어나 있던 개발 커밋을 다시 연결한다」라고 하는 사용법. 다른 하나는 「여러 개의 커밋을 1 커밋으로 정리한다」라고 하는 사용법이다. 각각 살펴보자. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;개발 커밋을 다시 연결하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;먼저 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각각의 브랜치로 늘어나 있던 개발 커밋을 다시 연결한다」를 살펴보도록 하자.&lt;/span&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HA4nI/dJMcachuf3k/HVRqvCR7OFFSQoIvOIXsJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HA4nI/dJMcachuf3k/HVRqvCR7OFFSQoIvOIXsJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HA4nI/dJMcachuf3k/HVRqvCR7OFFSQoIvOIXsJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHA4nI%2FdJMcachuf3k%2FHVRqvCR7OFFSQoIvOIXsJ1%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 상태에서「브랜치 A」를 「브랜치 B」에 머지할 필요가 생겼다고 하자. 그냥&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 머지한다면 아래와 같이 될 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQ5XZ/dJMcafZzTyb/Zpon5Sx8OUyHBbxciCFLZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQ5XZ/dJMcafZzTyb/Zpon5Sx8OUyHBbxciCFLZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQ5XZ/dJMcafZzTyb/Zpon5Sx8OUyHBbxciCFLZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQ5XZ%2FdJMcafZzTyb%2FZpon5Sx8OUyHBbxciCFLZK%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 브랜치 A의 끝에서 브랜치 B의 갱신 내용을 신규 커밋으로서 다시 생성할 수 있는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;머지와의차이를그림으로살펴보도록하겠다. &lt;/span&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chND3C/dJMcafZzTyY/QwU1qkwVeqK7ceiYTcBl41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chND3C/dJMcafZzTyY/QwU1qkwVeqK7ceiYTcBl41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chND3C/dJMcafZzTyY/QwU1qkwVeqK7ceiYTcBl41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchND3C%2FdJMcafZzTyY%2FQwU1qkwVeqK7ceiYTcBl41%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; rebase 쪽이 갱신 로그가 일직선으로 보기 쉽다는 것을 알 수 있다.&amp;nbsp; 과거의 갱신 이력을 쫓을 때도 보기 편해진다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이처럼 브랜치마다 진행되고 있는 개발을 다시 연결하고 청소하는 것이 'rebase 명령어'의 기능 중 하나이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans 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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;커맨드 설명을 위해 브랜치 B를 사용하고 있는 것으로 가정하겠다. 그 상태에서 이하의 명령을 실행하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git rebase [연결 대상 브랜치명]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rebase 명령어 뒤에 연결할 브랜치 이름을 붙이기만 하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참고로 커밋 ID 를 지정할 수도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예로 위에서 봤던 이미지를 구현한다고 하자.&lt;/span&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r8UnT/dJMcadAEsr0/8uJQtiLYAOx0J6nKqz2gnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r8UnT/dJMcadAEsr0/8uJQtiLYAOx0J6nKqz2gnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r8UnT/dJMcadAEsr0/8uJQtiLYAOx0J6nKqz2gnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr8UnT%2FdJMcadAEsr0%2F8uJQtiLYAOx0J6nKqz2gnk%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt;(브랜치 B에 체크아웃한 상태) 그럼 커맨드는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git rebase 브랜치A&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;여러 개의 커밋을 1개의 커밋으로 정리하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;또 하나 다름 사용법인 여러 개의 커밋을 하나로 정리하는 방법에 대해서 알아보도록 하자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;이번에는 브랜치A를 사용하고 있다고 가정하다. 그리고 아래와 같은 개발이 진행되고 있다고 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccmSNq/dJMcabplfz8/N5aCZKqjtmTtcZu7epuY90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccmSNq/dJMcabplfz8/N5aCZKqjtmTtcZu7epuY90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccmSNq/dJMcabplfz8/N5aCZKqjtmTtcZu7epuY90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccmSNq%2FdJMcabplfz8%2FN5aCZKqjtmTtcZu7epuY90%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이 4개의 커밋 ABCD가 존재하고 각 수정은 매우 미비한 수정으로 「한 묶음으로 하고 싶다&amp;hellip;」라고 하는 생각이 머리를 스쳤을 때는&amp;hellip;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rebase 명령어가 사용하면 된다. rebase 명령의 &quot;-i&quot; 옵션을 이용하면 복수 커밋을 1 커밋으로 정리할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQBMiu%2FdJMcabv7sYg%2FLuHjMARnwaHrE60XLGz90k%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 이런 식으로 「신규 커밋 E」로서 지정한 복수의 커밋을 하나로 다시 만들 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans 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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 개의 커밋을 하나의 커밋으로 정리하려면 , 「-i 옵션」을 사용한 이하의 커맨드를 실행할 필요가 있다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git rebase - i [묶음으로 하는 지점 중 하나 앞의 커밋 ID] [묶음으로 하는 지점 중 하나 앞의 커밋 ID]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&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-origin-width=&quot;1000&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQBMiu/dJMcabv7sYg/LuHjMARnwaHrE60XLGz90k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQBMiu%2FdJMcabv7sYg%2FLuHjMARnwaHrE60XLGz90k%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;1000&quot; height=&quot;563&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;563&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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 이미지를 구현하는 것이라면 「개시 지점 커밋의 커밋 ID」를 지정할 필요가 있다. 그것을 지정함으로써 '개시지점 커밋 ~ 현재 위치'까지의 '커밋 ABCD'를 하나로 묶을 수 있게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또 해당 커맨드를 친 직후, 어느 커밋을 한 묶음으로 할지의 지정 화면이 표시된다. 딱 아래와 같은 화면이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;pick d9a1f0e 커밋A
pick 400f340 커밋B
pick f35185d 커밋C
pick 8dfc486 커밋D&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 이 화면에서 어떤 커밋을 하나로 묶을지 지정하자.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합할 커밋의 'pick(아무것도 하지 않음)' 부분을 'squash(통합함)'로 변경하고 저장 후 종료하면 통합이 실행되게 된다. 다만 이 때, 선두의 커밋은 통합 시작될 부분이 되기 때문에 pick인 채로 해야한다. 그 점을 주의하자.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;pick d9a1f0e 커밋A
squash 400f340 커밋B
squash f35185d 커밋C
squash 8dfc486 커밋B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 전부 통합하고자한다면 이렇게 설정하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;merge와 rebase의 차이점&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr 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 Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &amp;nbsp;두 명령어의 가장 큰 차이점은 '기존 커밋에 영향을 주는가, 주지 않는가'이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음에 설명한 대로 rebase 명령어는 처리를 다시 만들어 버리는 반면 &quot;merge 명령어&quot;는 기존 커밋에 영향을 주지 않고 머지 커밋을 작성해 준다. 이 점이 이 두 가지의 가장 큰 차이점이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이 차이로 인해 「팀 개발」을 실시하고 있을 때 혼란을 야기하게 되는 경우가 발생할 수도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;팀 개발 측면에서 봤을 때&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr 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 Sans Demilight, Noto Sans KR;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어느 날, 자신이 올렸던 커밋이 없어져 버렸다! 그렇게 되면, 매우 곤란해진다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 공동 개발을 하고 있는 이상 'rebase 명령어'를 안이하게 사용하면 누군가의 커밋을 마음대로 다시 만들게 되어 팀에 혼란을 초래하게 되므로 주의가 필요하다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://www.sejuku.net/blog/71919&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;https://www.sejuku.net/blog/71919&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/451</guid>
      <comments>https://engineer-mole.tistory.com/451#entry451comment</comments>
      <pubDate>Tue, 6 Jan 2026 23:09:49 +0900</pubDate>
    </item>
    <item>
      <title>git revert</title>
      <link>https://engineer-mole.tistory.com/450</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;※&amp;nbsp;&lt;/span&gt;일본의&amp;nbsp;한&amp;nbsp;블로그&amp;nbsp;글을&amp;nbsp;번역한&amp;nbsp;포스트입니다.&amp;nbsp;오역&amp;nbsp;및&amp;nbsp;의역,&amp;nbsp;직역이&amp;nbsp;있을&amp;nbsp;수&amp;nbsp;있으며&amp;nbsp;틀린&amp;nbsp;내용은&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;revert란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&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 Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;기존 커밋을 취소하기 위한 명령어로,&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;「취소하고 싶은 커밋을 취소하는 커밋을 새롭게 작성한다」라고 하는 처리로 기존의 커밋을 취소한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;새로 커밋을 추가하고 있을 뿐이지 기존 커밋의 이력이 사라지는 것은 아니다(커밋 로그를 보시면 남아있다).&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 변경이 있었는가 하는 것이 (revert한 것을 포함하여) 남기 때문에 원격으로 push되어 공개되어 있는 커밋에 대해서도 안전하게 사용할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;'기존 커밋을 원래대로 되돌린다'는 점에 대해서 비슷한 기능을 가진 명령어에 reset이 존재한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다만, reset 명령어의 경우 커밋을 취소했다는 커밋이 남지 않는다는 차이가 있다. 따라서 리모트에 push가 되어 공개되어 있는 커밋에 관해서 사용하는 것은 좋지 않다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한 revert 명령어는 특정 커밋을 원래대로 되돌리는 명령어이기도 하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;지정한 커밋 이후에 행해진 처리를 모두 되돌리는(이 기능은 reset 명령어에 해당됨) 것은 아니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;커맨드&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;특정 커밋을 취소하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1767705179315&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git revert &amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 커밋 ID를 지정함으로써 그 커밋을 취소하는 커밋이 새롭게 추가된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코드는 해당 커밋이 없었을 때의 상태가 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;revert 명령을 실행하면 에디터가 열려 커밋 메시지를 편집할 수 있다. &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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;커밋메시지 편집&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; revert 커밋을 실시할 때, 커밋 메시지의 편집을 실시할지 말지를 옵션으로 지정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;커밋 메시지를 편집할 경우&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1767705264973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git revert &amp;lt;commit&amp;gt; -e 

or

$ git revert &amp;lt;commit&amp;gt; --edit&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;커밋 메시지를 편집하지 않을 경우&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1767705316722&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git revert &amp;lt;commit&amp;gt; --no-edit&lt;/code&gt;&lt;/pre&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;커밋하지 않기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; revert 명령어를 사용하면 commit까지 행해지지만 index에 되돌리기만 하면 commit을 행하지 않도록 할 수도 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 옵션을 사용할 경우 여러 커밋을 revert할 때 한번에 커밋할 수 있어 편리하다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767705402888&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git revert &amp;lt;commit&amp;gt; -n

or

$ git revert &amp;lt;commit&amp;gt; --no-commit&lt;/code&gt;&lt;/pre&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;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;머지 커밋&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;머지 커밋을 취소하려고 할 경우 병합한 2개의 커밋(부모) 중 어느 쪽으로 되돌릴 것인지 지정해야 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;-m옵션 뒤로 되돌리려는 부모를 숫자(기본적으로 1 혹은 2)로 지정하고 revert를 실행한다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767705464762&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git revert -m 1 &amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고로 git show 명령어나 git log 명령어로 대상 머지커밋을 보면 부모의 숫자(1 혹은 2)를 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1767705502644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git show
commit xyz
Merge: 1a1a1a 2b2b2b    #여기를 주목
Author: xxxx xxxx
Date:   Thu Jul 13 09:00:00 2017 +0000

    Merge commit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; revert 하고 싶은 머지커밋이 이 xyz라고 하는 번호의 커밋이라고 가정하자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 머지 커밋은 '1a1a1a'라는 커밋에 '2b2b2b'라는 커밋이 병합되어 생긴 것이다(#여기를 주목 부분).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;번호는 로그를 표시했을 때 'Merge:'라고 하는 행에 쓰여져 있는 순서대로 1, 2가 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다시 말해 이 경우는, 「1a1a1a」가 번호 1, 「2b2b2b」는 번호 2가 된다. &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 Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://qiita.com/chihiro/items/2fa827d0eac98109e7ee&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://qiita.com/chihiro/items/2fa827d0eac98109e7ee&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/기초 지식</category>
      <author>개발자 두더지</author>
      <guid isPermaLink="true">https://engineer-mole.tistory.com/450</guid>
      <comments>https://engineer-mole.tistory.com/450#entry450comment</comments>
      <pubDate>Tue, 6 Jan 2026 22:20:23 +0900</pubDate>
    </item>
  </channel>
</rss>