<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>지직전기</title>
    <link>https://msh103.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 13 Jun 2026 14:33:08 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>MSH103</managingEditor>
    <image>
      <title>지직전기</title>
      <url>https://tistory1.daumcdn.net/tistory/6779239/attach/642b9d4a403a48aa98b7b17317acae60</url>
      <link>https://msh103.tistory.com</link>
    </image>
    <item>
      <title>[C#/WPF] UI 업데이트 최적화 &amp;ndash; Dispatcher.Invoke/BeginInvoke와 스레드 마샬링</title>
      <link>https://msh103.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-3&quot; data-message-id=&quot;5d02dc48-8076-4e98-904c-b1dabc1bd1d9&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;blockquote data-end=&quot;40&quot; data-start=&quot;0&quot; data-ke-size=&quot;size23&quot; data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;WPF에서 UI를 갱신할 때 Dispatcher를 어떻게 사용해야 할까?&lt;/b&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-end=&quot;202&quot; data-start=&quot;42&quot; data-ke-size=&quot;size16&quot;&gt;WPF로 프로그램을 처음 개발하다보니 UI 스레드와 백그라운드 스레드의 역할 차이, 그리고 데이터를 전달하는 과정을 제대로 이해하지 못해 무분별하게 혼용 사용하여 리팩토링 간 고생했던 기억이 있습니다. 새로 만들던 프로그램의 요구사항이 한번에 지도에 50000+ 개가 넘는 항적을 UI로 표시할 수 있도록 만드는게 목표였기 때문에 성능 개선에 좀 더 관심을 갖기 위해 아래 내용을 찾아보고 정리해보았습니다.&lt;/p&gt;
&lt;p data-end=&quot;202&quot; data-start=&quot;42&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;289&quot; data-start=&quot;204&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 단순하게 백그라운드에서 데이터를 받으면 Dispatcher를 통해 UI를 바로 업데이트하면 되지 않을까 하며 무분별한 Invoke와 BeginInvoke를 남발했는데 그로 인해 성능 저하가 발생하고(매 프레임간 버벅거림 발생)&lt;/p&gt;
&lt;p data-end=&quot;289&quot; data-start=&quot;204&quot; data-ke-size=&quot;size16&quot;&gt;특히 네트워크, 센서, 타이머처럼 이벤트가 지속적으로 발생하는 환경에서는 UI를 어떻게 갱신하느냐에 따라 체감 성능이 크게 바뀐다는 것을 알게 되었습니다.&lt;/p&gt;
&lt;p data-end=&quot;327&quot; data-start=&quot;291&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;332&quot; data-start=&quot;329&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;351&quot; data-start=&quot;334&quot; data-ke-size=&quot;size20&quot;&gt;UI(메인)스레드와 작업(백그라운드) 스레드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;555&quot; data-start=&quot;472&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;513&quot; data-start=&quot;472&quot; data-section-id=&quot;ecp2y3&quot;&gt;백그라운드 스레드 - 데이터 수집, 생성&lt;br /&gt;네트워크 수신, 파일 I/O, 연산 처리 담당&lt;/li&gt;
&lt;li data-end=&quot;555&quot; data-start=&quot;515&quot; data-section-id=&quot;1tnfxe&quot;&gt;UI 스레드 - UI 컨트롤&lt;br /&gt;화면 렌더링, 사용자 입력, 컨트롤 업데이트 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;599&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 WPF에서는 모든 UI 요소가 UI 스레드에 종속됩니다.&lt;/p&gt;
&lt;p data-end=&quot;599&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;다른 스레드에서 UI를 직접 접근하면 InvalidOperationException이 발생합니다.&lt;/p&gt;
&lt;p data-end=&quot;599&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;백그라운드 스레드에서는 Dispatcher를 통해 마샬링하여 UI 업데이트를 요청하거나 버퍼/큐에 담아 UI스레드에 데이터를 전달하여 UI 업데이트를 요청할 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;604&quot; data-start=&quot;601&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;641&quot; data-start=&quot;606&quot; data-ke-size=&quot;size20&quot;&gt;Dispatcher에서 Invoke / BeginInvoke의 동작&lt;/h4&gt;
&lt;p data-end=&quot;692&quot; data-start=&quot;643&quot; data-ke-size=&quot;size16&quot;&gt;백그라운드 스레드에서 UI를 건드리려면 Dispatcher를 통해 작업을 넘겨야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;692&quot; data-start=&quot;643&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;749&quot; data-start=&quot;694&quot; data-ke-size=&quot;size16&quot;&gt;먼저 Invoke는 동기 호출입니다.&lt;/p&gt;
&lt;p data-end=&quot;749&quot; data-start=&quot;694&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Invoke로 보낸 UI 업데이트 작업은 UI스레드가 즉시 수행하며 작업이 끝날때까지 호출한 스레드는 대기상태에 있습니다.(작업 순서 보장)&lt;/span&gt;&lt;br /&gt;따라서 이로 인해 Invoke로 과도한 메세지를 보낼 경우 Deadlock 현상에 빠질 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;749&quot; data-start=&quot;694&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;818&quot; data-start=&quot;751&quot; data-ke-size=&quot;size16&quot;&gt;BeginInvoke는 비동기 호출입니다.&lt;br /&gt;UI 스레드에 작업만 요청하고 즉시 리턴되며, Dispatcher 큐에 작업을 등록합니다.&lt;/p&gt;
&lt;p data-end=&quot;818&quot; data-start=&quot;751&quot; data-ke-size=&quot;size16&quot;&gt;UI스레드는 큐에 쌓인 작업을 순차적으로 처리합니다(호출은 빠르나 작업 순서 보장 X)&lt;/p&gt;
&lt;p data-end=&quot;818&quot; data-start=&quot;751&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;여기서 제가 오해했던 부분이&amp;nbsp;BeginInvoke는 큐에 넣고 빠지는 거라 스레드간 충돌도 없고 괜찮지 않을까? 였는데&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;실제로는 호출 스레드를 막지 않을 뿐 UI 스레드의 부담을 줄여주는 것은 아닌걸로 이해했습니다.&lt;/p&gt;
&lt;hr data-end=&quot;905&quot; data-start=&quot;902&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;915&quot; data-start=&quot;907&quot; data-ke-size=&quot;size20&quot;&gt;트러블 슈팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문제점&lt;/p&gt;
&lt;p data-end=&quot;993&quot; data-start=&quot;917&quot; data-ke-size=&quot;size16&quot;&gt;개발 간 프로그램에 항적 리스트를 넣고 데이터가 들어오면 실시간으로 업데이트하는 기능을 만든 적이 있습니다.&lt;br /&gt;항적 시뮬레이터에서는 초당 수백 건(700~800건) 이상의 메시지가 들어오는 구조였습니다.&lt;/p&gt;
&lt;p data-end=&quot;1009&quot; data-start=&quot;995&quot; data-ke-size=&quot;size16&quot;&gt;처음 구현은 단순했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1078&quot; data-start=&quot;1011&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1023&quot; data-start=&quot;1011&quot; data-section-id=&quot;utg8dk&quot;&gt;메시지 수신 시마다 Dispatcher.BeginInvoke 호출&lt;/li&gt;
&lt;li data-end=&quot;1078&quot; data-start=&quot;1052&quot; data-section-id=&quot;at2r6x&quot;&gt;항적리스트에 실시간으로 UI 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1108&quot; data-start=&quot;1080&quot; data-ke-size=&quot;size16&quot;&gt;코드는 단순했고 초기에는 정상적으로 동작했지만&amp;nbsp;몇 초 지나지 않아 문제가 발생했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1180&quot; data-start=&quot;1138&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1151&quot; data-start=&quot;1138&quot; data-section-id=&quot;1kyp3p8&quot;&gt;리스트 스크롤이 밀림&lt;/li&gt;
&lt;li data-end=&quot;1162&quot; data-start=&quot;1152&quot; data-section-id=&quot;of3i94&quot;&gt;클릭 반응 지연&lt;/li&gt;
&lt;li data-end=&quot;1180&quot; data-start=&quot;1163&quot; data-section-id=&quot;wbu1mw&quot;&gt;UI 전체가 버벅거리는 현상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1232&quot; data-start=&quot;1182&quot; data-ke-size=&quot;size16&quot;&gt;디버깅 결과 Dispatcher 큐에 수천 개 이상의 delegate가 쌓여 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1279&quot; data-start=&quot;1251&quot; data-ke-size=&quot;size16&quot;&gt;문제의 원인은&lt;/p&gt;
&lt;p data-end=&quot;1279&quot; data-start=&quot;1251&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &quot;이벤트 발생 횟수만큼 UI 작업을 쌓고 있었다.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1279&quot; data-start=&quot;1251&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1299&quot; data-start=&quot;1286&quot; data-ke-size=&quot;size18&quot;&gt;왜 이런 문제가 발생할까&lt;/p&gt;
&lt;p data-end=&quot;1362&quot; data-start=&quot;1301&quot; data-ke-size=&quot;size16&quot;&gt;BeginInvoke는 호출 자체는 가볍지만 UI 스레드는 그 모든 작업을 결국 하나씩 처리해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1431&quot; data-start=&quot;1368&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1405&quot; data-start=&quot;1368&quot; data-section-id=&quot;e4hr9l&quot;&gt;이벤트 1000건 발생 &amp;rarr; delegate 1000개 큐에 적재&lt;/li&gt;
&lt;li data-end=&quot;1431&quot; data-start=&quot;1406&quot; data-section-id=&quot;fcfe77&quot;&gt;UI 스레드 &amp;rarr; 1000번 UI 갱신 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1473&quot; data-start=&quot;1457&quot; data-ke-size=&quot;size16&quot;&gt;여러 스레드에서 UI라는 하나의 자원을 향해 계속 작업을 밀어 넣는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;일종의 병목 문제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1519&quot; data-start=&quot;1475&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1526&quot; data-end=&quot;1546&quot; data-ke-size=&quot;size18&quot;&gt;해결 방법: UI 갱신 빈도를 줄이기(DispaterTimer)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1526&quot; data-end=&quot;1546&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-start=&quot;1560&quot; data-end=&quot;1591&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;ldquo;이벤트 발생 빈도와 UI 갱신 빈도를 분리해야 한다.&amp;rdquo;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1593&quot; data-end=&quot;1644&quot; data-ke-size=&quot;size16&quot;&gt;백그라운드에서는 데이터를 계속 받아도 되지만,&lt;br /&gt;UI는 일정 주기로만 업데이트하면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1593&quot; data-end=&quot;1644&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1651&quot; data-end=&quot;1656&quot; data-ke-size=&quot;size18&quot;&gt;구현 방식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1658&quot; data-end=&quot;1693&quot; data-ke-size=&quot;size16&quot;&gt;백그라운드에서는 UI를 건드리지 않고 큐에만 데이터를 쌓습니다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&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;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ConcurrentQueue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;_queue&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;OnDataReceived&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt;_queue&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Enqueue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&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&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;1838&quot; data-end=&quot;1887&quot; data-ke-size=&quot;size16&quot;&gt;UI 스레드에서는 DispatcherTimer를 사용해 일정 주기로 한 번에 처리합니다.&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&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;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;DispatcherTimer&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;_uiTimer&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ObservableCollection&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Items&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;{&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;; }&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;MainViewModel&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt;_uiTimer&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;DispatcherTimer&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt;Interval&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;TimeSpan&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;FromMilliseconds&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;_uiTimer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Tick&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;FlushUiUpdates&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span&gt;_uiTimer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Start&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;FlushUiUpdates&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;/span&gt;&lt;span&gt;_queue&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TryDequeue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt;Items&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&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&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;2321&quot; data-end=&quot;2340&quot; data-ke-size=&quot;size16&quot;&gt;이 방식의 특징은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;2342&quot; data-end=&quot;2379&quot;&gt;
&lt;li data-section-id=&quot;m8fcty&quot; data-start=&quot;2342&quot; data-end=&quot;2356&quot;&gt;이벤트는 계속 쌓이지만 UI는 100ms마다 한 번만 갱신됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;2381&quot; data-end=&quot;2386&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;2388&quot; data-end=&quot;2440&quot;&gt;
&lt;li data-section-id=&quot;167v2lq&quot; data-start=&quot;2388&quot; data-end=&quot;2418&quot;&gt;초당 수천 이벤트 &amp;rarr; UI는 초당 10회 이하 갱신(UI 부하 급감 &amp;rarr; 체감 성능 개선)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2445&quot; data-start=&quot;2442&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2477&quot; data-start=&quot;2447&quot; data-ke-size=&quot;size20&quot;&gt;Invoke / BeginInvoke는 언제 써야 할까&lt;/h4&gt;
&lt;p data-end=&quot;2511&quot; data-start=&quot;2479&quot; data-ke-size=&quot;size16&quot;&gt;모든 UI 갱신을 타이머로 처리할 수 있는 것은 아닙니다.&lt;/p&gt;
&lt;p data-end=&quot;2538&quot; data-start=&quot;2513&quot; data-ke-size=&quot;size16&quot;&gt;즉시 반영이 필요한 경우는 여전히 존재합니다.&lt;/p&gt;
&lt;p data-end=&quot;2538&quot; data-start=&quot;2513&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2538&quot; data-start=&quot;2513&quot;&gt;Dispatcher를 통해 업데이트 되는 값이 다음 코드에서 계산할 때 필요한 경우&lt;/li&gt;
&lt;/ul&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&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;Dispatcher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Invoke&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TextBox&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;값 설정&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;// 이 아래 코드가 TextBox.Text 값에 의존하는 경우&lt;/span&gt;&lt;br /&gt;&lt;span&gt;DoSomething&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;TextBox&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;);&lt;/span&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&gt;
&lt;div&gt;이런 경우에는 Invoke를 사용하는 것이 맞습니다.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2538&quot; data-start=&quot;2513&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2546&quot; data-start=&quot;2540&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2597&quot; data-start=&quot;2548&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2559&quot; data-start=&quot;2548&quot; data-section-id=&quot;jx7th9&quot;&gt;경고 메시지 표시&lt;/li&gt;
&lt;li data-end=&quot;2573&quot; data-start=&quot;2560&quot; data-section-id=&quot;iflg85&quot;&gt;버튼 상태 즉시 변경&lt;/li&gt;
&lt;li data-end=&quot;2597&quot; data-start=&quot;2574&quot; data-section-id=&quot;vllsl4&quot;&gt;사용자 인터랙션에 대한 즉각적인 피드백&lt;/li&gt;
&lt;/ul&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&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Current&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Dispatcher&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BeginInvoke&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StatusText&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;긴급 알림 발생&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span&gt; 이런 경우에는 BeginInvoke를 사용하는 것이 맞습니다. &lt;/span&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&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2753&quot; data-start=&quot;2726&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;2758&quot; data-start=&quot;2755&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2762&quot; data-start=&quot;2760&quot; data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 23일 오전 10_38_37.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvmUBy/dJMcadBGdHG/dFXXoLkVhf60ojZOZGIEp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvmUBy/dJMcadBGdHG/dFXXoLkVhf60ojZOZGIEp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvmUBy/dJMcadBGdHG/dFXXoLkVhf60ojZOZGIEp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvmUBy%2FdJMcadBGdHG%2FdFXXoLkVhf60ojZOZGIEp0%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;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2026년 4월 23일 오전 10_38_37.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;2832&quot; data-start=&quot;2764&quot; data-ke-size=&quot;size16&quot;&gt;WPF에서 UI는 반드시 UI 스레드에서만 접근해야 합니다.&lt;br /&gt;이를 위해 Dispatcher를 통한 마샬링이 필요합니다.&lt;/p&gt;
&lt;p data-end=&quot;2900&quot; data-start=&quot;2834&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이벤트마다 Invoke / BeginInvoke를 호출하는 구조는&lt;br /&gt;UI 스레드에 과도한 부담을 주게 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;2930&quot; data-start=&quot;2902&quot; data-ke-size=&quot;size16&quot;&gt;따라서 구조를 다음과 같이 나누는 것이 중요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3011&quot; data-start=&quot;2932&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2953&quot; data-start=&quot;2932&quot; data-section-id=&quot;1fahrae&quot;&gt;백그라운드 스레드 &amp;rarr; 데이터만 수집&lt;/li&gt;
&lt;li data-end=&quot;2980&quot; data-start=&quot;2954&quot; data-section-id=&quot;189bi0e&quot;&gt;UI 스레드 &amp;rarr; 타이머 기반으로 주기적 갱신&lt;/li&gt;
&lt;li data-end=&quot;3011&quot; data-start=&quot;2981&quot; data-section-id=&quot;xovy67&quot;&gt;즉시 반영이 필요한 경우만 Dispatcher 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;3065&quot; data-start=&quot;3013&quot; data-ke-size=&quot;size16&quot;&gt;이 방식으로 구조를 바꾸면,&lt;br /&gt;실시간 이벤트가 많은 환경에서도 UI가 안정적으로 유지됩니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/C#</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/110</guid>
      <comments>https://msh103.tistory.com/110#entry110comment</comments>
      <pubDate>Thu, 23 Apr 2026 10:49:21 +0900</pubDate>
    </item>
    <item>
      <title>[C#/WPF] MVVM + DDD패턴 적용하기</title>
      <link>https://msh103.tistory.com/113</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-end=&quot;20&quot; data-start=&quot;0&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MVVM + DDD 패턴은 무엇일까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;170&quot; data-start=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;WPF로 프로그램을 개발하다 보면 자연스럽게 MVVM 패턴을 사용하게 된다.&lt;br /&gt;여기에 규모가 커지고 비즈니스 로직이 복잡해지면 단순 MVVM 구조만으로는 관리가 어려워진다.&lt;br /&gt;이때 많이 같이 사용하는 것이 DDD(Domain-Driven Design)이다.&lt;/p&gt;
&lt;p data-end=&quot;227&quot; data-start=&quot;172&quot; data-ke-size=&quot;size16&quot;&gt;두 패턴은 서로 다른 문제를 해결하지만, 함께 사용하면 구조가 명확해지고 유지보수가 훨씬 쉬워진다.&lt;/p&gt;
&lt;hr data-end=&quot;232&quot; data-start=&quot;229&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;250&quot; data-start=&quot;234&quot; data-ke-size=&quot;size20&quot;&gt;MVVM과 DDD의 역할 차이&lt;/h4&gt;
&lt;p data-end=&quot;279&quot; data-start=&quot;252&quot; data-ke-size=&quot;size16&quot;&gt;먼저 두 패턴의 역할부터 정리해보면 다음과 같다.(내용이 길어 접은 글로 대체)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;MVVM 패턴이란? - UI 구조를 분리하는 패턴&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-end=&quot;264&quot; data-start=&quot;51&quot; data-ke-size=&quot;size16&quot;&gt;MVVM(Model&amp;ndash;View&amp;ndash;ViewModel)은 UI(화면)와 비즈니스 로직을 분리하기 위한 아키텍처 패턴이다. 쉽게 말해 화면(View), 화면을 제어하는 중간 관리자(ViewModel), 실제 데이터와 로직(Model)을 각각 역할에 맞게 나누어 서로 직접 의존하지 않도록 만드는 구조이다.&lt;/p&gt;
&lt;p data-end=&quot;264&quot; data-start=&quot;51&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCC9sf/dJMb99MHSNL/01s7YnyTwtZhQbfTtJd2AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCC9sf/dJMb99MHSNL/01s7YnyTwtZhQbfTtJd2AK/img.png&quot; data-alt=&quot;MVVM패턴 구조 - 출처 : wikipedia&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCC9sf/dJMb99MHSNL/01s7YnyTwtZhQbfTtJd2AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCC9sf%2FdJMb99MHSNL%2F01s7YnyTwtZhQbfTtJd2AK%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;500&quot; height=&quot;150&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MVVM패턴 구조 - 출처 : wikipedia&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-end=&quot;669&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;MVVM은 세 가지 요소로 구성된다.&lt;/p&gt;
&lt;p data-end=&quot;669&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;1. View&lt;/p&gt;
&lt;p data-end=&quot;669&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;View는 XAML로 작성되는 화면 영역으로, 버튼이나 텍스트 같은 UI 요소를 담당하며 사용자 입력을 받고 데이터를 표시하는 역할을 한다. 중요한 특징은 View에는 비즈니스 로직이 거의 존재하지 않으며, ViewModel에 바인딩하는 역할만 수행한다는 점이다.&lt;/p&gt;
&lt;p data-end=&quot;669&quot; data-start=&quot;495&quot; data-ke-size=&quot;size16&quot;&gt;2. ViewModel&lt;/p&gt;
&lt;p data-end=&quot;911&quot; data-start=&quot;671&quot; data-ke-size=&quot;size16&quot;&gt;ViewModel은 MVVM에서 가장 핵심적인 역할을 담당한다. View와 Model 사이에서 중간 관리자 역할을 하며, View에 표시할 데이터를 가공하고 Command를 통해 사용자 입력을 처리하며 상태를 관리한다. 특히 ViewModel은 View를 직접 참조하지 않고 바인딩을 통해 연결된다는 점이 중요하다. 쉽게 말해 ViewModel은 &amp;ldquo;화면이 쓰기 좋은 형태로 데이터를 만들어주는 계층&amp;rdquo;이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;911&quot; data-start=&quot;671&quot; data-ke-size=&quot;size16&quot;&gt;3. Model&lt;/p&gt;
&lt;p data-end=&quot;1081&quot; data-start=&quot;913&quot; data-ke-size=&quot;size16&quot;&gt;Model은 실제 데이터와 비즈니스 로직을 담당하는 영역이다. 데이터 저장, 유효성 검사, 계산 등의 역할을 수행하며, 나이 계산이나 특정 조건 판단 같은 로직이 여기에 포함된다. MVVM에서 Model은 단순한 데이터 객체일 수도 있고, DDD 관점에서는 도메인 모델로 확장될 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;1081&quot; data-start=&quot;913&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1427&quot; data-start=&quot;1083&quot; data-ke-size=&quot;size16&quot;&gt;MVVM의 핵심 구조는 View와 ViewModel이 바인딩으로 연결되고, ViewModel이 Model을 사용하는 형태이다. 즉, View는 ViewModel과 바인딩으로 연결되어 데이터를 주고받고, ViewModel은 코드로 Model을 호출하여 실제 로직을 수행한다. 이 구조에서 가장 중요한 개념이 바로 바인딩이다. 예를 들어 TextBox의 Text를 ViewModel의 속성과 연결하거나, Button의 클릭을 Command로 연결하면, View는 직접 ViewModel을 호출하지 않아도 자동으로 데이터와 이벤트가 전달된다. 이 덕분에 코드비하인드를 최소화하고 UI와 로직을 효과적으로 분리할 수 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DDD패턴이란? - 비즈니스 로직 구조를 분리하는 패턴&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-end=&quot;175&quot; data-start=&quot;45&quot; data-ke-size=&quot;size16&quot;&gt;DDD(Domain&amp;ndash;Driven Design)는 비즈니스 로직을 중심으로 시스템을 설계하기 위한 아키텍처 접근 방식이다. 쉽게 말해 화면이나 데이터베이스가 아니라, 실제 업무 개념(도메인)을 기준으로 구조를 나누고 설계하는 방법이다.&lt;/p&gt;
&lt;p data-end=&quot;175&quot; data-start=&quot;45&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 24일 오후 01_49_21.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LToE0/dJMcaaLP6aq/a30f6qSRFLZeuvVtGOsBkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LToE0/dJMcaaLP6aq/a30f6qSRFLZeuvVtGOsBkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LToE0/dJMcaaLP6aq/a30f6qSRFLZeuvVtGOsBkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLToE0%2FdJMcaaLP6aq%2Fa30f6qSRFLZeuvVtGOsBkK%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;644&quot; height=&quot;429&quot; data-filename=&quot;ChatGPT Image 2026년 4월 24일 오후 01_49_21.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;202&quot; data-start=&quot;180&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;202&quot; data-start=&quot;180&quot; data-ke-size=&quot;size16&quot;&gt;DDD는 주요 계층과 개념으로 구성된다.&lt;/p&gt;
&lt;p data-end=&quot;202&quot; data-start=&quot;180&quot; data-ke-size=&quot;size16&quot;&gt;1.Domain&lt;/p&gt;
&lt;p data-end=&quot;373&quot; data-start=&quot;215&quot; data-ke-size=&quot;size16&quot;&gt;Domain은 시스템의 핵심 비즈니스 로직이 위치하는 영역이다. 실제 업무 규칙과 상태를 표현하며, 엔티티(Entity), 값 객체(Value Object), 도메인 서비스(Domain Service) 등이 포함된다. 중요한 특징은 도메인이 UI나 인프라에 의존하지 않는다는 점이다.&lt;/p&gt;
&lt;p data-end=&quot;373&quot; data-start=&quot;215&quot; data-ke-size=&quot;size16&quot;&gt;2.Application&lt;/p&gt;
&lt;p data-end=&quot;539&quot; data-start=&quot;391&quot; data-ke-size=&quot;size16&quot;&gt;Application은 도메인 로직을 활용하여 실제 기능 흐름을 구성하는 계층이다. 사용자의 요청을 받아 도메인 객체를 조합하고 실행 순서를 제어하며, 트랜잭션이나 작업 단위를 관리한다. 비즈니스 규칙 자체는 포함하지 않고, &amp;ldquo;무엇을 할지&amp;rdquo;를 정의하는 역할을 한다.&lt;/p&gt;
&lt;p data-end=&quot;539&quot; data-start=&quot;391&quot; data-ke-size=&quot;size16&quot;&gt;3.Infrastructure&lt;/p&gt;
&lt;p data-end=&quot;701&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;Infrastructure는 데이터베이스, 네트워크, 파일 등 외부 시스템과의 연결을 담당하는 계층이다. Repository 구현, API 통신, 하드웨어 제어 등이 여기에 포함된다. 도메인 계층이 직접 외부 시스템에 의존하지 않도록 중간 역할을 한다.&lt;/p&gt;
&lt;p data-end=&quot;701&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;894&quot; data-start=&quot;706&quot; data-ke-size=&quot;size16&quot;&gt;DDD의 핵심 구조는 Application이 Domain을 사용하고, Infrastructure가 Domain을 지원하는 형태이다. 즉, 도메인이 중심에 있고 나머지 계층이 이를 둘러싸는 구조이다. 이때 중요한 개념이 유비쿼터스 언어(Ubiquitous Language)로, 코드에서 사용하는 용어를 실제 업무 용어와 일치시키는 것이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;988&quot; data-start=&quot;899&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 DDD는 시스템을 기술 중심이 아니라 비즈니스 개념 중심으로 설계하고, 핵심 로직을 Domain에 집중시켜 유지보수성과 확장성을 높이는 설계 방식이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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;div&gt;
&lt;div data-message-model-slug=&quot;gpt-5-3&quot; data-message-id=&quot;87dba9b8-f450-4d2f-9ab4-24069229829e&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;509&quot; data-ke-size=&quot;size18&quot;&gt;왜 같이 써야 할까&lt;/p&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;521&quot; data-ke-size=&quot;size16&quot;&gt;MVVM만 사용할 경우 흔히 발생하는 문제가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;606&quot; data-start=&quot;551&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;571&quot; data-start=&quot;551&quot; data-section-id=&quot;o805i6&quot;&gt;ViewModel이 점점 비대해짐&lt;/li&gt;
&lt;li data-end=&quot;596&quot; data-start=&quot;572&quot; data-section-id=&quot;1atqa2x&quot;&gt;비즈니스 로직이 ViewModel에 섞임&lt;/li&gt;
&lt;li data-end=&quot;606&quot; data-start=&quot;597&quot; data-section-id=&quot;st2weg&quot;&gt;테스트 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;613&quot; data-start=&quot;608&quot; data-ke-size=&quot;size16&quot;&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;&amp;nbsp;&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;div&gt;&lt;span&gt;// ViewModel에 비즈니스 로직이 섞인 경우&lt;/span&gt;&lt;br /&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TrackTarget&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Target&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Distance&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Speed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; {&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;// 추적 조건 판단 (비즈니스 로직)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&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&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;830&quot; data-start=&quot;782&quot; data-ke-size=&quot;size16&quot;&gt;이 상태가 계속 쌓이면 ViewModel이 &amp;ldquo;UI + 비즈니스&amp;rdquo;를 모두 담당하게 된다.&lt;/p&gt;
&lt;p data-end=&quot;862&quot; data-start=&quot;832&quot; data-ke-size=&quot;size16&quot;&gt;DDD를 적용하면 이 로직을 Domain으로 분리한다.&lt;/p&gt;
&lt;hr data-end=&quot;867&quot; data-start=&quot;864&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;882&quot; data-start=&quot;869&quot; data-ke-size=&quot;size20&quot;&gt;MVVM + DDD 구조&lt;/h4&gt;
&lt;p data-end=&quot;902&quot; data-start=&quot;884&quot; data-ke-size=&quot;size16&quot;&gt;전체 구조는 보통 이렇게 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;969&quot; data-start=&quot;904&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;917&quot; data-start=&quot;904&quot; data-section-id=&quot;19gd7mk&quot;&gt;View (XAML)&lt;/li&gt;
&lt;li data-end=&quot;929&quot; data-start=&quot;918&quot; data-section-id=&quot;tg6way&quot;&gt;ViewModel&lt;/li&gt;
&lt;li data-end=&quot;929&quot; data-start=&quot;918&quot; data-section-id=&quot;tg6way&quot;&gt;&lt;s&gt;Model&lt;/s&gt;(DDD로 대체)&lt;/li&gt;
&lt;li data-end=&quot;943&quot; data-start=&quot;930&quot; data-section-id=&quot;15o510y&quot;&gt;Application&lt;/li&gt;
&lt;li data-end=&quot;952&quot; data-start=&quot;944&quot; data-section-id=&quot;1u6oeko&quot;&gt;Domain&lt;/li&gt;
&lt;li data-end=&quot;969&quot; data-start=&quot;953&quot; data-section-id=&quot;117mrvz&quot;&gt;Infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;982&quot; data-start=&quot;971&quot; data-ke-size=&quot;size16&quot;&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&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span&gt;View &amp;rarr; ViewModel &amp;rarr; Application &amp;rarr; Domain&lt;/span&gt;&lt;br /&gt;&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; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr;&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; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;uarr;&lt;/span&gt;&lt;br /&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; &amp;nbsp; &amp;nbsp;Infrastructure&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 」&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;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1095&quot; data-start=&quot;1092&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;1104&quot; data-start=&quot;1097&quot; data-ke-size=&quot;size20&quot;&gt;각 계층 역할&lt;/h4&gt;
&lt;p data-end=&quot;1110&quot; data-start=&quot;1106&quot; data-ke-size=&quot;size18&quot;&gt;View&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1128&quot; data-start=&quot;1112&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1120&quot; data-start=&quot;1112&quot; data-section-id=&quot;mwv5md&quot;&gt;UI만 담당&lt;/li&gt;
&lt;li data-end=&quot;1128&quot; data-start=&quot;1121&quot; data-section-id=&quot;9206tb&quot;&gt;로직 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1139&quot; data-start=&quot;1130&quot; data-ke-size=&quot;size18&quot;&gt;ViewModel&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1181&quot; data-start=&quot;1141&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1151&quot; data-start=&quot;1141&quot; data-section-id=&quot;jlou79&quot;&gt;UI 상태 관리&lt;/li&gt;
&lt;li data-end=&quot;1164&quot; data-start=&quot;1152&quot; data-section-id=&quot;xi89y3&quot;&gt;Command 처리&lt;/li&gt;
&lt;li data-end=&quot;1181&quot; data-start=&quot;1165&quot; data-section-id=&quot;14u2chi&quot;&gt;Application 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777013300469&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MainViewModel
{
private readonly TrackingAppService _service;

public ICommand TrackCommand =&amp;gt; new RelayCommand(() =&amp;gt;
{
_service.Track();
});
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1390&quot; data-start=&quot;1379&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1390&quot; data-start=&quot;1379&quot; data-ke-size=&quot;size18&quot;&gt;Application&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1417&quot; data-start=&quot;1392&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1405&quot; data-start=&quot;1392&quot; data-section-id=&quot;1lxihol&quot;&gt;유스케이스 흐름 정의&lt;/li&gt;
&lt;li data-end=&quot;1417&quot; data-start=&quot;1406&quot; data-section-id=&quot;1eu1ils&quot;&gt;도메인 객체 조합&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777013310968&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TrackingAppService
{
private readonly ITargetRepository _repo;

public void Track()
{
var target = _repo.Get();
target.StartTracking();
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1625&quot; data-start=&quot;1619&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1625&quot; data-start=&quot;1619&quot; data-ke-size=&quot;size18&quot;&gt;Domain&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1639&quot; data-start=&quot;1627&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1639&quot; data-start=&quot;1627&quot; data-section-id=&quot;1htie5o&quot;&gt;핵심 비즈니스 로직&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777013322082&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Target
{
public double Distance { get; set; }
public double Speed { get; set; }

public bool CanTrack()
{
return Distance &amp;gt; 1000 &amp;amp;&amp;amp; Speed &amp;gt; 50;
}

public void StartTracking()
{
if (!CanTrack())
throw new InvalidOperationException();
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1980&quot; data-start=&quot;1966&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1980&quot; data-start=&quot;1966&quot; data-ke-size=&quot;size18&quot;&gt;Infrastructure&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1999&quot; data-start=&quot;1982&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1999&quot; data-start=&quot;1982&quot; data-section-id=&quot;1qcp08q&quot;&gt;DB, 네트워크, 장비 연동&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;pre id=&quot;code_1777013333866&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class TargetRepository : ITargetRepository
{
public Target Get()
{
// 외부 데이터 가져오기
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2132&quot; data-start=&quot;2129&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2140&quot; data-start=&quot;2134&quot; data-ke-size=&quot;size20&quot;&gt;핵심 포인트&lt;/h4&gt;
&lt;p data-end=&quot;2166&quot; data-start=&quot;2142&quot; data-ke-size=&quot;size16&quot;&gt;이 구조에서 가장 중요한 건 의존 방향이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2236&quot; data-start=&quot;2168&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2202&quot; data-start=&quot;2168&quot; data-section-id=&quot;od6rsg&quot;&gt;ViewModel &amp;rarr; Application &amp;rarr; Domain&lt;/li&gt;
&lt;li data-end=&quot;2236&quot; data-start=&quot;2203&quot; data-section-id=&quot;1i0qlel&quot;&gt;Infrastructure &amp;rarr; Domain (구현 제공)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2260&quot; data-start=&quot;2238&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Domain은 어디에도 의존하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2311&quot; data-start=&quot;2266&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2288&quot; data-start=&quot;2266&quot; data-section-id=&quot;cbvod1&quot;&gt;UI 변경 &amp;rarr; Domain 영향 없음&lt;/li&gt;
&lt;li data-end=&quot;2311&quot; data-start=&quot;2289&quot; data-section-id=&quot;2iu9gv&quot;&gt;DB 변경 &amp;rarr; Domain 영향 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2316&quot; data-start=&quot;2313&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2330&quot; data-start=&quot;2318&quot; data-ke-size=&quot;size20&quot;&gt;실무에서 체감되는 장점&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2489&quot; data-start=&quot;2332&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2374&quot; data-start=&quot;2332&quot; data-section-id=&quot;6o8t4t&quot;&gt;ViewModel이 가벼워짐&lt;br /&gt;UI 로직만 남아서 구조가 깔끔해진다.&lt;/li&gt;
&lt;li data-end=&quot;2418&quot; data-start=&quot;2376&quot; data-section-id=&quot;1oudrqh&quot;&gt;테스트가 쉬워짐&lt;br /&gt;Domain은 순수 로직이라 단위 테스트 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;2457&quot; data-start=&quot;2420&quot; data-section-id=&quot;99kmfu&quot;&gt;변경에 강함&lt;br /&gt;UI나 DB가 바뀌어도 핵심 로직은 유지된다.&lt;/li&gt;
&lt;li data-end=&quot;2489&quot; data-start=&quot;2459&quot; data-section-id=&quot;112v8x6&quot;&gt;협업이 쉬워짐&lt;br /&gt;도메인 기준으로 역할이 나뉜다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;2494&quot; data-start=&quot;2491&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2501&quot; data-start=&quot;2496&quot; data-ke-size=&quot;size20&quot;&gt;주의할 점&lt;/h4&gt;
&lt;p data-end=&quot;2527&quot; data-start=&quot;2503&quot; data-ke-size=&quot;size16&quot;&gt;처음부터 과하게 적용하면 오히려 복잡해진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2573&quot; data-start=&quot;2529&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2551&quot; data-start=&quot;2529&quot; data-section-id=&quot;1vupbsn&quot;&gt;작은 프로젝트 &amp;rarr; MVVM만으로 충분&lt;/li&gt;
&lt;li data-end=&quot;2573&quot; data-start=&quot;2552&quot; data-section-id=&quot;ty3i6z&quot;&gt;도메인이 복잡할 때 &amp;rarr; DDD 도입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2617&quot; data-start=&quot;2575&quot; data-ke-size=&quot;size16&quot;&gt;또한 Application과 Domain의 역할을 섞지 않는 것이 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;2622&quot; data-start=&quot;2619&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;2626&quot; data-start=&quot;2624&quot; data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 24일 오후 03_43_07.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O42Dr/dJMcahKX5sv/6PShbi21gc6gkBq1IB53B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O42Dr/dJMcahKX5sv/6PShbi21gc6gkBq1IB53B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O42Dr/dJMcahKX5sv/6PShbi21gc6gkBq1IB53B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO42Dr%2FdJMcahKX5sv%2F6PShbi21gc6gkBq1IB53B0%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;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2026년 4월 24일 오후 03_43_07.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;2678&quot; data-start=&quot;2628&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2678&quot; data-start=&quot;2628&quot; data-ke-size=&quot;size16&quot;&gt;MVVM은 UI 구조를 정리하는 패턴이고,&lt;br /&gt;DDD는 비즈니스 로직을 정리하는 패턴이다.&lt;/p&gt;
&lt;p data-end=&quot;2690&quot; data-start=&quot;2680&quot; data-ke-size=&quot;size16&quot;&gt;둘을 함께 사용하면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2736&quot; data-start=&quot;2692&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2710&quot; data-start=&quot;2692&quot; data-section-id=&quot;18ot6th&quot;&gt;UI와 로직이 완전히 분리되고&lt;/li&gt;
&lt;li data-end=&quot;2722&quot; data-start=&quot;2711&quot; data-section-id=&quot;8gqyow&quot;&gt;구조가 명확해지며&lt;/li&gt;
&lt;li data-end=&quot;2736&quot; data-start=&quot;2723&quot; data-section-id=&quot;14dykve&quot;&gt;유지보수가 쉬워진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2750&quot; data-start=&quot;2738&quot; data-ke-size=&quot;size16&quot;&gt;결국 핵심은 이것이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;2797&quot; data-start=&quot;2752&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ViewModel에는 UI만,&lt;br /&gt;비즈니스 로직은 Domain에 두는 것.&lt;/b&gt;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;2797&quot; data-start=&quot;2752&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Model을 하나의 덩어리로 두지 않고,&lt;br /&gt;Domain / Application / Infrastructure로 역할을 나누는 것&lt;/b&gt;&lt;/p&gt;</description>
      <category>STUDY/C#</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/113</guid>
      <comments>https://msh103.tistory.com/113#entry113comment</comments>
      <pubDate>Thu, 9 Apr 2026 17:38:09 +0900</pubDate>
    </item>
    <item>
      <title>[Book 리뷰]한 걸음 앞선 개발자가 지금 꼭 알아야 할 클로드 코드</title>
      <link>https://msh103.tistory.com/111</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20250923_225310604.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXBQI3/btsQMZPCyfn/6kZTnSIYk07tyVRjZ5jxTk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXBQI3/btsQMZPCyfn/6kZTnSIYk07tyVRjZ5jxTk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXBQI3/btsQMZPCyfn/6kZTnSIYk07tyVRjZ5jxTk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXBQI3%2FbtsQMZPCyfn%2F6kZTnSIYk07tyVRjZ5jxTk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;660&quot; data-filename=&quot;KakaoTalk_20250923_225310604.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 읽어본 『한 걸음 앞선 개발자가 지금 꼭 알아야 할 클로드 코드』에 대해 리뷰를 적어보려합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 목표였던 책 3권 읽기를 이제야 시작하게 되었는데, 벌써 10월이 다 되어가네요.. &lt;br /&gt;그래도 시작이 반이니, 올해가 가기 전 목표치까지 리뷰를 채워보도록 열심히 달려보겠습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;책을 읽게된 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 회사에서 1년정도 근무하면서 C++ / MFC 기반으로 프로그램 개발을 하고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 신규 프로젝트를 맡게되었는데 리더를 맡으신 차장님께서 MFC보다 라이브러리도 많고 레퍼런스할 자료들도 많은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# / WPF를 이용해 개발하는 방향으로 적극적으로 밀어붙이셨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 개발 기간이 길지 않아 C#을 새로 배우면서 적용하기가 빠듯했고 Copilot 등 AI 도구를 적극적으로 활용하길 권장하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하면서 공부해라 식으로 진행하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Github Copilot를 이용하여 Claude.Sonnet 모델을 맛보기로나마 사용하고 있었는데 구독을 해도 사용량이 많지않아 답답함을 느끼고 있던 중 예전에 '코딩자율학습단'을 진행했던 길벗 출판사에서 먼저 읽어보고 평가하는 서평단 이벤트를 진행하여 마침 Claude 모델을 쓰고 있는데 조금 더 활용해볼 수 있는 좋은 기회다 싶어 신청하였고 운 좋게 선정되어 해당 책을 제공받아 읽어 볼 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;책 리뷰&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 ClaudeCode의 설치부터 기본적인 가이드, 아키텍처 설계부터 코드 리뷰까지 효율적으로 활용하는 방법까지 단계별로 잘 구성되어 있습니다. 특히 평일 30분 ~ 1시간씩 4주동안 학습할 수 있도록 커리큘럼이 나뉘어 있어 직장인도 큰 부담없이 진도에 맞게 읽을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 지난 주 수요일부터 퇴근하고 매일 챕터만큼 학습하고 있는데 책에 적힌대로 따라가기 어렵지않고 부담없이 학습할 수 있었습니다.(시키기만 하면 Claude가 다 해줘요 ) 게다가 주말은 학습이 아닌 읽을거리를 제공해주는데 내용도 재밌고 쏠쏠합니다!&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;1주차에는 WSL을 이용해 리눅스를 설치하고 거기에 클로드 코드를 설치해 기본적인 사용법과 제공받은 코드를 이용해 간단한 프로젝트를 개발해보는 과정이었고 귀여운 댄싱 고양이를 만드는 마지막 1-5챕터에서 귀여운 댄싱 고양이를 만드는 프로젝트까지 진행해보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vF5g2/btsQMihQqiI/JLlokxi0iC72F97mJHIUmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vF5g2/btsQMihQqiI/JLlokxi0iC72F97mJHIUmk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;321&quot; data-filename=&quot;설치.png&quot; style=&quot;width: 75.5893%; margin-right: 10px;&quot; data-widthpercent=&quot;76.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vF5g2/btsQMihQqiI/JLlokxi0iC72F97mJHIUmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvF5g2%2FbtsQMihQqiI%2FJLlokxi0iC72F97mJHIUmk%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;955&quot; height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcZG8Y/btsQL80Pgnk/hF97q2XxSFaZf10I4utXA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcZG8Y/btsQL80Pgnk/hF97q2XxSFaZf10I4utXA0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;800&quot; data-filename=&quot;클로드활용2.png&quot; style=&quot;width: 23.2479%;&quot; data-widthpercent=&quot;23.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcZG8Y/btsQL80Pgnk/hF97q2XxSFaZf10I4utXA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcZG8Y%2FbtsQL80Pgnk%2FhF97q2XxSFaZf10I4utXA0%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;732&quot; height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;클로드 코드 시작(좌) / 1-5챕터 댄싱고양이 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 주차에서는 클로드 코드 활용성을 높히기 위한 주요 기능 실습, 추가 프로젝트 진행, 워크플로 전략 및 클로드 코드 효율 극대화하기 등의 심화학습으로 진행된다고 하는데 초심을 잃지않고 쭉 완주하여 실무에서도 클로드 코드로 대체하여 사용할 수 있도록 활용 능력을 길러보도록 노력해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;총평&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로드코드가 2025년 5월 이후 본격적으로 상용화되면서 새로운 기능이 꾸준히 추가&amp;middot;개선되고 있기 때문에, 이 책을 기반으로 꾸준히 업데이트된 지식을 흡수한다면 개발할 때 훌륭한 보조 도구로 자리잡을 수 있을 거라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;P.S 책에서 소개된 설치 방법은 WSL을 이용한 리눅스 기반 설치였는데, 찾아보니 6월 패치부터는 윈도우에서도 직접 설치가 가능하더라구요. 그래서 회사에서는 윈도우에 바로 ClaudeCode를 설치해 사용 중인데, 개인적으로는 이쪽이 훨씬 편리했습니다.&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;이 책은 단순히 AI 코드 도구 사용법을 넘어, ClaudeCode를 실무에 적용하고 싶은 개발자에게 체계적인 가이드를 제공하는 훌륭한 안내서라고 할 수 있습니다. 저처럼 Copilot만 쓰던 분들이라면 이 책을 통해 ClaudeCode의 가능성을 제대로 느껴보실 수 있을 겁니다.&lt;/p&gt;</description>
      <category>STUDY/Book</category>
      <category>서평단 #협찬</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/111</guid>
      <comments>https://msh103.tistory.com/111#entry111comment</comments>
      <pubDate>Wed, 24 Sep 2025 00:45:15 +0900</pubDate>
    </item>
    <item>
      <title>[최적화] OpenGL 렌더링 성능향상 - Display List</title>
      <link>https://msh103.tistory.com/107</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFyLxD/btsNowylLp9/kceydtclljIvKlnDUtMDNk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFyLxD/btsNowylLp9/kceydtclljIvKlnDUtMDNk/img.gif&quot; style=&quot;width: 48.6924%; margin-right: 10px;&quot; data-widthpercent=&quot;49.27&quot; data-is-animation=&quot;true&quot; data-filename=&quot;렌더링개선 전.gif&quot; data-origin-height=&quot;349&quot; data-origin-width=&quot;741&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFyLxD/btsNowylLp9/kceydtclljIvKlnDUtMDNk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFyLxD%2FbtsNowylLp9%2FkceydtclljIvKlnDUtMDNk%2Fimg.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;741&quot; height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LueOz/btsNqgz01Pd/yVSqvCuIJO4JdfOB1jhVZk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LueOz/btsNqgz01Pd/yVSqvCuIJO4JdfOB1jhVZk/img.gif&quot; style=&quot;width: 50.1448%;&quot; data-widthpercent=&quot;50.73&quot; data-is-animation=&quot;true&quot; data-filename=&quot;렌더링개선 후.gif&quot; data-origin-height=&quot;327&quot; data-origin-width=&quot;715&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LueOz/btsNqgz01Pd/yVSqvCuIJO4JdfOB1jhVZk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLueOz%2FbtsNqgz01Pd%2FyVSqvCuIJO4JdfOB1jhVZk%2Fimg.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;715&quot; height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;성능 최적화 이전(좌), 이후(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;문제 발생&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;343&quot; data-start=&quot;239&quot; data-ke-size=&quot;size16&quot;&gt;프로그램을 다중 실행하거나, 지도에 표시되는 오브젝트(Shapefile 등)의 개수가 많아질수록&lt;br /&gt;지도 이동이나 확대/축소 시 전시 화면이 뚝뚝 끊기는 현상이 발생.&lt;/p&gt;
&lt;p data-end=&quot;343&quot; data-start=&quot;239&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 기존 3ms 내외였던 전시 주기가 최대 200~300ms까지 지연&lt;/p&gt;
&lt;p data-end=&quot;343&quot; data-start=&quot;239&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 마우스 반응도 느려지고, 전체적인 사용자 경험(UX) 저하&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;원인 분석&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenGL을 이용해 맵에 지형 및 오브젝트 등 항목데이터를 실시간으로 렌더링하고 있는데 렌더링하는 함수(RenderScene()) 내부에서 ShapeFile 도형 정보(라인, 폴리곤) 등의 정보를 매 프레임마다 초기화 후 다시 렌더링하는 방식(Immediate Mode)으로 동작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;즉, 매 프레임마다 동일한 도형을 반복적으로 매번 그리게 되어 특히 지도 이동, 확대/축소 명령 시 병목현상이 일어나는 것으로 파악(매 프레임마다 CPU &amp;rarr; GPU로 렌더링 명령을 반복 전송)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenGL의 Display List 기능을 활용하여 자주 그리는 도형(Shapefile) 정보를 미리 컴파일해 저장해두고,&lt;br /&gt;필요할 때 glCallList()로 빠르게 재사용하는 구조로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Shapefile 데이터를 처음 불러올 때 도형별로 Display List를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 렌더링 시점에 glCallList로 호출만하게 하여 성능 최적화&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;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 66px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-start=&quot;1670&quot; data-end=&quot;1878&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;항&amp;nbsp; 목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;변경 전&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;변경 후&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-start=&quot;1722&quot; data-end=&quot;1777&quot;&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1722&quot; data-end=&quot;1746&quot;&gt;평균 렌더링 시간 (객체 70개 기준)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1746&quot; data-end=&quot;1762&quot;&gt;160~300ms&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1762&quot; data-end=&quot;1777&quot;&gt;30~50ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-start=&quot;1778&quot; data-end=&quot;1812&quot;&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1778&quot; data-end=&quot;1792&quot;&gt;지도 이동 시 반응성&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1792&quot; data-end=&quot;1800&quot;&gt;뚝뚝 끊김&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; data-start=&quot;1800&quot; data-end=&quot;1812&quot;&gt;부드러운 스크롤&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745157959451&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void GraphicsManager::RenderScene(void)
{
 &amp;hellip;(생략)
ShapefileGroup* pShapefileGroup = NULL;
ShapefileGroupVectorIter groupIter;
ShapefilePair* pShapefilePair = NULL;
ShapefilePairVectorIter pairIter;

groupIter = m_ShapefileGroupVector.begin();
while (groupIter != m_ShapefileGroupVector.end())
{
	pShapefileGroup = (ShapefileGroup*)*groupIter;

	if (pShapefileGroup &amp;amp;&amp;amp; pShapefileGroup-&amp;gt;isVisible)
	{
		pairIter = pShapefileGroup-&amp;gt;shapefilePairVector.begin();
		while (pairIter != pShapefileGroup-&amp;gt;shapefilePairVector.end())
		{

			pShapefilePair = (ShapefilePair*)*pairIter;
			if (pShapefilePair)
			{
				if (!pShapefilePair-&amp;gt;displayListCreated)
				{
					BuildShapefileDisplayList(pShapefilePair); // Display List 생성
				}

				::glColor3f(pShapefilePair-&amp;gt;color.r / 255.0f * m_Brightness,
					pShapefilePair-&amp;gt;color.g / 255.0f * m_Brightness,
					pShapefilePair-&amp;gt;color.b / 255.0f * m_Brightness);

				//RenderShapefile(pShapefilePair-&amp;gt;converted); &amp;rarr; 기존 코드(여기서 매 프레임마다 렌더링 수행)
				RenderShapefileFromList(pShapefilePair); // Display List 내 렌더링된 지형정보 불러오기

			}
			++pairIter;
		}

	}
	++groupIter;
}
 &amp;hellip;(생략)
}

void GraphicsManager::BuildShapefileDisplayList(ShapefilePair* pair)
{
	if (!pair || pair-&amp;gt;displayListCreated || !pair-&amp;gt;converted.records || pair-&amp;gt;converted.numRecords &amp;lt;= 0)
		return;

	pair-&amp;gt;glDisplayList = glGenLists(1); 
	glNewList(pair-&amp;gt;glDisplayList, GL_COMPILE); // 새로운 DisplayList 생성
	
	// 기존 RenderShapefile(pShapefilePair-&amp;gt;converted)을 여기에서 수행
	for (int recNum = 0; recNum &amp;lt; pair-&amp;gt;converted.numRecords; ++recNum)
	{
		SHAPEFILE_RECORD* recPtr = pair-&amp;gt;converted.records[recNum];
		if (!recPtr) continue;

		if (recPtr-&amp;gt;shapeType == SHAPE_TYPE_POINT)
		{
		}
		else
		{
			for (int partNum = 0; partNum &amp;lt; recPtr-&amp;gt;numParts; ++partNum)
			{
				SHAPEFILE_PART&amp;amp; part = recPtr-&amp;gt;parts[partNum];

				// 타입별로 glBegin 설정
				if (recPtr-&amp;gt;shapeType == SHAPE_TYPE_POLYGON || recPtr-&amp;gt;shapeType == SHAPE_TYPE_POLYLINE)
				{
					glBegin(GL_LINE_STRIP);
				}
				else
				{
					switch (part.partType)
					{
					case SHAPE_PARTTYPE_TRIANGLE_STRIP:
						glBegin(GL_TRIANGLE_STRIP);
						break;
					case SHAPE_PARTTYPE_TRIANGLE_FAN:
						glBegin(GL_TRIANGLE_FAN);
						break;
					case 6: // 삼각형
						glBegin(GL_TRIANGLES);
						break;
					default:
						continue;
					}
				}

				for (int i = 0; i &amp;lt; part.numPoints; ++i)
				{
					glVertex2d(part.points[i].x, part.points[i].y);
				}

				glEnd();
			}
		}
	}

void GraphicsManager::RenderShapefileFromList(ShapefilePair* pair)
{
	if (!pair || !pair-&amp;gt;displayListCreated)
		return;

	glCallList(pair-&amp;gt;glDisplayList);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>STUDY/Troubleshooting</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/107</guid>
      <comments>https://msh103.tistory.com/107#entry107comment</comments>
      <pubDate>Sun, 20 Apr 2025 23:10:58 +0900</pubDate>
    </item>
    <item>
      <title>[트러블 슈팅] 항적 관리 간 삭제, 업데이트 충돌 문제 - Race Condition(경쟁 상태)</title>
      <link>https://msh103.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&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 Light'; color: #000000;&quot;&gt;프로그램의 항적관리 기능 중 삭제 명령 시 기존 항적이 삭제되고 항적번호가 1 증가한 새로운 항적이 생성되어야하는데 간헐적으로 기존 항적이 삭제되지않고 남아있음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&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 Light'; color: #000000;&quot;&gt;1. 프로그램에서 항적이 전시되려면 타 체계에서 보낸 항적 정보가 담긴 메세지 구조체를 받아 우리 서버를 거쳐 Client로 보내는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;2. 메세지는 100ms 간격으로 들어오고 주기적으로 들어오는 메세지 정보를 받아 항적 정보(위치, 항적번호 등)를 업데이트하며 삭제가 아닌 메세지 정보가 끊기면 미상 항적으로 60초간 전시했다 삭제되는 방식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;3. 타 체계에서 A라는 메세지를 보내다가 삭제 명령이 들어오면 B라는 메세지를 보내주는데 A가 출발한 상태에서 삭제메세지가 들어와 B라는 메세지를 보내주지만 이미 출발한 A라는 메세지는 그대로 Client까지 들어와 지도에 60초간 전시되며 삭제&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;257&quot; data-start=&quot;234&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;Race Condition이란?&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;257&quot; data-start=&quot;234&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&amp;ldquo;둘 이상의 비동기 작업이 순서에 따라 서로 영향을 줄 수 있는 상황&amp;rdquo;에서,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&amp;ldquo;그 순서가 예측 불가능하거나 통제되지 않는 경우 발생하는 문제&amp;rdquo;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;348&quot; data-start=&quot;261&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;468&quot; data-start=&quot;355&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;382&quot; data-start=&quot;355&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;동시에 일어나는 처리 흐름이 있고, &amp;rarr; map으로 항적을 관리하고 여기서 삭제 및 갱신 작업이 일어남(공유자원)&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;408&quot; data-start=&quot;383&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;그 순서가 결과에 영향을 주며, &lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&amp;rarr;&amp;nbsp;&lt;/span&gt; 제대로 삭제가 이루어 진 후 업데이트 되어야 정상 작동함&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;468&quot; data-start=&quot;409&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;순서를 보장하지 못한다. &lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&amp;rarr; 간헐적으로 순서가 충돌나면 위와 같은 문제가 발생&lt;/span&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 Light'; color: #000000;&quot;&gt;즉, 삭제명령과 업데이트 명령 두 개의 비동기 작업간 일어난 Race Condition - 순서 충돌 문제로 판단하였음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&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 Light'; color: #000000;&quot;&gt;1. Race Condition을 해결하는 방법 중에 뮤텍스를 이용한 방법이 있다길래 항적을 관리하는 Map을 뮤텍스로 묶어 제어 - 실패&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&amp;nbsp;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&amp;rarr; 뮤텍스는 하나의 공유자원에 동시에 접근하지 못하게 하는 기능으로 동시충돌제어에는 해결방법이 될 수 있으나, 우선 순위를 보장해주지는 못함.&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 Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;2. 우리측 서버에서 2차 검증 로직으로 DeleteList를 만들어 관리&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 Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp; 가.&amp;nbsp; 삭제 명령 시 Delete List에 해당 항적 번호를 등록&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 Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp; 나.&amp;nbsp; 갱신 명령 시 Delete List에 해당 항적 번호가 있는 지 조회 후 남아있다면 추가 삭제&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 Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp; 다. 일정 시간 이후 Delete List에서 해당 항적 번호 삭제(300ms)&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 Light'; color: #000000;&quot;&gt;&amp;nbsp;&lt;span style=&quot;text-align: left;&quot;&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;font-family: 'Noto Sans Light'; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr; 업데이트 구간 삭제 시 뮤텍스 없는 삭제 명령 함수를 만들어 해결&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>STUDY/Troubleshooting</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/108</guid>
      <comments>https://msh103.tistory.com/108#entry108comment</comments>
      <pubDate>Fri, 18 Apr 2025 09:38:09 +0900</pubDate>
    </item>
    <item>
      <title>2024년 회고 및 2025년 목표</title>
      <link>https://msh103.tistory.com/103</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;2024년을 돌아보며&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2024년은 저에게 수많은 도전과 용기가 필요했던 한 해였습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;10년간 복무했던 군을 떠나 새로운 시작을 다짐하며 개발자가 되기 위한 국비교육에 참여했고, 자소서와 이력서를 준비하며 본격적인 취업 활동에 나섰습니다. 취업 후에는 새로운 분야를 배우고 실무와 과제를 익히며 하루하루를 바쁘게 보내왔습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 과정에서 전역 전에 충분히 준비하지 못했던 스스로에 대한 아쉬움과&amp;nbsp; '과연 개발자가 될 수 있을까?' 하는 확신의 부족으로 많은 고민과 갈등도 있었지만 꾸준히 나아가다보니 취업도 되고 현재는 회사에 잘 적응하여 다행히 목표했던 직업 전환에 성공 할 수있지 않았나 생각합니다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2024년 성과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;작년 초에 블로그를 개설하며 2024년 Plan을 만들었던 적이 있습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;요약해보면&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. 자격증 : 정보처리기사, SQLD, OPIC IH, CCNP, AWS&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 깃허브(월 15회), 블로그 공부내용 정리&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. 코딩테스트 준비 : 백준(골드), 프로그래머스(PCCP) 자격증&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4. CS 기초 다지기 : 관련 책 3권 이상 읽기&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;당시에 이직에 대한 열망으로 목표를 마구마구 높게 잡았네요.. &lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2024년을 마치며 결산해보면&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. 자격증은 정보처리기사와 SQLD까지 취득하였고&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 깃허브는 포기. 국비 교육동안 배웠던 공부 내용은 블로그에 주차별로 정리했었고&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. 코딩테스트는 안했고.. &lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4. 관련 책을 3권 읽진 않았지만 코딩자율학습단 참여와 인프런 인강 등으로 공부했었습니다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;총평 : ⭐5/10&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;소감 : &lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;“&lt;/span&gt; 목표를 모두 완수하진 못했으나 목표가 있으니 뭐라도 하게되어 좋았다.”&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;2025년 다짐&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2025년은 자기개발도 중요하지만 입사한 지 얼마 되지 않은 신입으로서 회사의 실무를 먼저 익히고 실무 능력을 키우는 데 집중해야 할 시기라고 생각합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2024년의 경험을 반면교사 삼아 이번에는 현실적이면서도 스스로 동기부여가 될 수 있는 목표를 세워 꾸준히 실행해 나가려 합니다. 이를 통해 실무에서 성과를 내고 동시에 개인적으로도 성장할 수 있는 한 해를 만들고 싶습니다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2025년 목표&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. &lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;&lt;b&gt;사이드 프로젝트&lt;/b&gt; :&amp;nbsp; 2개를 완성하고, 깃허브와 블로그에 정리하기. &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. &lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;&lt;b&gt;영어 어학 자격증&lt;/b&gt; : 토익 700점 이상 또는 OPIC IM 취득하기. &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. &lt;b&gt;자기개발&lt;/b&gt; : 인터넷 강의, 관련 도서 읽기 또는 기타 자기개발 활동 3개 이상 실천하고 정리하기.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/103</guid>
      <comments>https://msh103.tistory.com/103#entry103comment</comments>
      <pubDate>Fri, 3 Jan 2025 00:17:42 +0900</pubDate>
    </item>
    <item>
      <title>[Socket] send()와 recv()는 1:1 매핑일까? feat 네이글알고리즘</title>
      <link>https://msh103.tistory.com/96</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;send()와 recv()는 1:1 매핑일까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;socket을 이용한 통신프로그램을 짜다보면 send()와 recv() 함수는 각각 데이터 송수신을 담당하는 중요한 역할을 하지만, 흔히 생각하는 것처럼 이 두 함수가 1:1로 매핑되지는 않습니다. send()를 호출하면 즉시 대응하는 recv()가 실행될 것이라고 예상할 수 있지만, 네트워크 상태, 소켓 버퍼 크기, 패킷 크기 등의 다양한 요소가 데이터 송수신의 흐름에 영향을 미칩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시코드는 채팅서버에서 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'라는 긴 채팅메세지를 for문을 통해 한글자씩 보내도록 만든 코드입니다.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&amp;lt;예시코드&amp;gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1725635806389&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;stdafx.h&quot;
#include &amp;lt;winsock2.h&amp;gt;
#pragma comment(lib, &quot;ws2_32&quot;)


int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsa = { 0 };
	if (::WSAStartup(MAKEWORD(2, 2), &amp;amp;wsa) != 0)
	{
		puts(&quot;ERROR: 윈속을 초기화 할 수 없습니다.&quot;);
		return 0;
	}

	//접속대기 소켓 생성
	SOCKET hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (hSocket == INVALID_SOCKET)
	{
		puts(&quot;ERROR: 소켓을 생성할 수 없습니다.&quot;);
		return 0;
	}

	//포트 바인딩 및 연결
	SOCKADDR_IN	svraddr = { 0 };
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = htons(25000);
	svraddr.sin_addr.S_un.S_addr = inet_addr(&quot;127.0.0.1&quot;);
	if (::connect(hSocket,
		(SOCKADDR*)&amp;amp;svraddr, sizeof(svraddr)) == SOCKET_ERROR)
	{
		puts(&quot;ERROR: 서버에 연결할 수 없습니다.&quot;);
		return 0;
	}

	//
	int nOpt = 1;
	::setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY,
		(char*)&amp;amp;nOpt, sizeof(nOpt));

	//채팅 메시지 송/수신
	char szBuffer[128] = { 0 };
	while (1)
	{
		//사용자로부터 문자열을 입력 받는다.
		gets_s(szBuffer);
		if (strcmp(szBuffer, &quot;EXIT&quot;) == 0)		break;

		//사용자가 입력한 문자열을 한글자씩 서버에 전송한다.
		int nLength = strlen(szBuffer);
		for (int i = 0; i &amp;lt; nLength; ++i)
			::send(hSocket, szBuffer + i, 1, 0);

		//서버로부터 방금 보낸 문자열에 대한 에코 메시지를 수신한다.
		memset(szBuffer, 0, sizeof(szBuffer));
		::recv(hSocket, szBuffer, sizeof(szBuffer), 0);
		printf(&quot;From server: %s\n&quot;, szBuffer);
	}

	//소켓을 닫고 종료.
	::shutdown(hSocket, SD_BOTH);
	::closesocket(hSocket);
	::WSACleanup();
	return 0;
}&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;Wireshark와 같은 네트워크 트래픽 분석 도구를 사용해 데이터를 분석해 보면, 실제로는 한 글자씩 전송되는 경우도 있지만, 여러 글자가 한 번에 전송되는 경우도 자주 관찰됩니다. 이것은 운영 체제와 네트워크에서 데이터를 효율적으로 처리하기 위한 최적화 기법 때문입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;네이글 알고리즘(Nagle Algorithm)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;br /&gt;기본&amp;nbsp;원리는&amp;nbsp;작은&amp;nbsp;데이터&amp;nbsp;패킷들을&amp;nbsp;모아서&amp;nbsp;더&amp;nbsp;큰&amp;nbsp;패킷으로&amp;nbsp;전송하는&amp;nbsp;것입니다. &lt;br /&gt;이를&amp;nbsp;통해&amp;nbsp;네트워크&amp;nbsp;트래픽을&amp;nbsp;줄이고&amp;nbsp;전송&amp;nbsp;효율을&amp;nbsp;높일&amp;nbsp;수&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;24인승 버스를 예로 들어 보겠습니다. 승객이 두 명만 탑승한 상태에서 목적지까지 이동하려면 빈 버스가 출발해야 합니다. 이는 효율적이지 않습니다. 네이글 알고리즘은 승객이 적당히 탈 때까지 버스가 기다렸다가 이동하는 것과 비슷합니다. 다만, 승객을 기다리는 동안 &lt;span style=&quot;color: #006dd7;&quot;&gt;지연이 발생&lt;/span&gt;할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;네이글&amp;nbsp;알고리즘&amp;nbsp;비활성화(TCP_NODELAY)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이글&amp;nbsp;알고리즘이&amp;nbsp;지연을&amp;nbsp;초래할&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문에,&amp;nbsp;실시간&amp;nbsp;응답이&amp;nbsp;중요한&amp;nbsp;애플리케이션에서는&amp;nbsp;TCP_NODELAY&amp;nbsp;옵션을&amp;nbsp;통해&amp;nbsp;이&amp;nbsp;알고리즘을&amp;nbsp;비활성화할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&amp;lt;예시&amp;nbsp;코드&amp;gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1725636595377&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 8080))

# 네이글 알고리즘 비활성화 옵션 설정
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

sock.send(b'Hello, World!')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 통해 즉각적인 응답을 요구하는 애플리케이션에서 지연을 최소화할 수 있지만, 네트워크 성능에 미치는 영향을 신중히 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 send()와 recv()는 1:1 매핑되지 않으며, 여러 네트워크 최적화 기법과 시스템 요소가 데이터 송수신에 영향을 미칩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이글 알고리즘은 작은 패킷을 모아서 전송하는 방식으로 네트워크 효율을 높이지만, 실시간 성능이 중요한 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP_NODELAY 옵션을 사용해 네이글 알고리즘을 비활성화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 언제나 네트워크 상황과 애플리케이션의 요구 사항을 고려하여 적절한 선택을 해야 합니다.&lt;/p&gt;</description>
      <category>STUDY/C, C++</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/96</guid>
      <comments>https://msh103.tistory.com/96#entry96comment</comments>
      <pubDate>Sat, 7 Sep 2024 00:31:30 +0900</pubDate>
    </item>
    <item>
      <title>TCP를 이용한 채팅서버 구축 #1 소켓 프로그래밍 이론(TCP/UDP)</title>
      <link>https://msh103.tistory.com/95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1B651/btsITfAkpRZ/BlRiINrIwn7ysK9lvnhuR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1B651/btsITfAkpRZ/BlRiINrIwn7ysK9lvnhuR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1B651/btsITfAkpRZ/BlRiINrIwn7ysK9lvnhuR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1B651%2FbtsITfAkpRZ%2FBlRiINrIwn7ysK9lvnhuR1%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;739&quot; height=&quot;342&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 프로그래밍 강의를 듣고 공부 내용을 정리해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서는 ①소켓프로그래밍 이론을 먼저 공부하고 ②Echo서버/Client를 구축해본다음 ③채팅서버를 만들고 최종적으로 ④IOCP모델을 기반으로 성능 개량까지 진행해볼 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;개발 및 테스트 환경&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 10&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 2022 Community&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark(패킷분석도구)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC 2대와 인터넷 공유기(유선 인터넷 연결설정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;소켓(Socket)이란?&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;소켓은 프로그램이 네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;소켓은 OS 커널에 구현되어 있는 프로토콜 요소에 대한 추상화된 인터페이스로,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;장치 파일의 일종으로 이해할 수 있으며 일반 파일에 대한 개념이 대부분 적용됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;소켓 API의 종류와 실행 흐름&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP 기반 채팅서버를 구축하는데 사용하는 소켓 API는 아래와 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;socket() : 소켓을 생성합니다. 소켓의 종류를 스트림(TCP)타입 / 데이터그램(UDP)타입 중에 선택할 수있습니다.&lt;/li&gt;
&lt;li&gt;bind() : 소켓에 특정 포트번호를 사용 할 수 있도록 결합합니다. 중복된 포트를 사용할 경우 bind되지 않습니다.&lt;/li&gt;
&lt;li&gt;listen() : 결합이 성공적으로 끝나면 해당 포트번호로 클라이언트 연결요청이 있는지 대기합니다.&lt;/li&gt;
&lt;li&gt;connect() : IP주소와 Port 번호로 식별되는 대상으로 연결 요청을 보냅니다.&lt;/li&gt;
&lt;li&gt;accept() : 클라이언트에서 커넥트 요청이 들어와 연결이 성공하면 데이터 송수신을 위한 소켓을 생성합니다.&lt;/li&gt;
&lt;li&gt;send() / recv() : 연결이 성공하면 데이터를 주고받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;shutdown() : 소켓의 송수신을 종료합니다. 자원해제는 close 되기 전까지 유효합니다.&lt;/li&gt;
&lt;li&gt;close() : 최종적으로 모든 연결을 닫아 종료합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXpYsM/btsIUtEm500/flSVHPK8KaKIrtEYpGe4zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXpYsM/btsIUtEm500/flSVHPK8KaKIrtEYpGe4zk/img.png&quot; data-alt=&quot;소켓 실행순서 좌:서버 / 우: 클라이언트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXpYsM/btsIUtEm500/flSVHPK8KaKIrtEYpGe4zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXpYsM%2FbtsIUtEm500%2FflSVHPK8KaKIrtEYpGe4zk%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;1075&quot; height=&quot;674&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소켓 실행순서 좌:서버 / 우: 클라이언트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트 소켓(Client Socket)은 처음 소켓을&lt;span&gt; &lt;/span&gt;&lt;b&gt;생성(socket)&lt;/b&gt;한 다음, 서버 측에&lt;span&gt; &lt;/span&gt;&lt;b&gt;연결(connect)&lt;/b&gt;을 요청합니다. 그리고 서버 소켓에서 연결이 받아들여지면 데이터를&lt;span&gt; &lt;/span&gt;&lt;b&gt;송수신(send/recv)&lt;/b&gt;하고, 모든 처리가 완료되면 소켓(Socket)을&lt;span&gt; &lt;/span&gt;&lt;b&gt;닫습니다(close).&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서버 소켓(Server Socket)은 클라이언트와 마찬가지로, 첫 번째 단계는 소켓을&lt;span&gt; &lt;/span&gt;&lt;b&gt;생성(&lt;b&gt;socket&lt;/b&gt;)&lt;/b&gt;하는 것입니다. 그리고 서버 소켓이 해야 할 두 번째 작업은, 서버가 사용할 IP 주소와 포트 번호를 생성한 소켓에&lt;span&gt; &lt;/span&gt;&lt;b&gt;결합(bind)&lt;/b&gt;시키는 것입니다. 그런 다음 클라이언트로부터 연결 요청이 수신되는지&lt;span&gt; &lt;/span&gt;&lt;b&gt;주시(listen)&lt;/b&gt;하고, 요청이 수신되면 요청을&lt;span&gt; &lt;/span&gt;&lt;b&gt;받아들여(accept)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터 통신을 위한 소켓을 생성합니다. 일단 새로운 소켓을 통해 연결이 수립(ESTABLISHED)되면, 클라이언트와 마찬가지로 데이터를&lt;span&gt; &lt;/span&gt;&lt;b&gt;송수신(send/recv)&lt;/b&gt;할 수 있습니다. 마지막으로 데이터 송수신이 완료되면, 소켓(Socket)을&lt;span&gt; &lt;/span&gt;&lt;b&gt;닫습니다(close)&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;TCP 상태다이어그램&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP 상태 다이어그램은 TCP 연결이 설정되고 종료되는 과정을 보여주는 도구로 TCP 연결의 각 상태와 그 상태 간의 전환을 나타냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUQv0R/btsITOoEUGO/yr0lJli6D6aPXUotjiKRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUQv0R/btsITOoEUGO/yr0lJli6D6aPXUotjiKRK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUQv0R/btsITOoEUGO/yr0lJli6D6aPXUotjiKRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUQv0R%2FbtsITOoEUGO%2Fyr0lJli6D6aPXUotjiKRK1%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;903&quot; height=&quot;558&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CLOSED
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 상태입니다. 소켓이 열리지 않은 상태입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LISTEN
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 소켓이 클라이언트의 연결 요청을 기다리는 상태입니다.&lt;/li&gt;
&lt;li&gt;서버가 socket()을 호출한 후 bind() 및 listen()을 호출하면 이 상태로 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SYN_SENT
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 서버에 연결 요청(SYN 패킷)을 보낸 후 응답을 기다리는 상태입니다.&lt;/li&gt;
&lt;li&gt;클라이언트가 connect()를 호출하면 이 상태로 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SYN_RECEIVED
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버가 클라이언트의 SYN 패킷을 받고, 이를 승인하는 ACK 패킷과 함께 자신의 SYN 패킷을 클라이언트에게 보낸 상태입니다.&lt;/li&gt;
&lt;li&gt;서버는 LISTEN 상태에서 클라이언트의 SYN 패킷을 받으면 이 상태로 전환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ESTABLISHED
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결이 설정된 상태로, 양쪽에서 데이터를 주고받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;클라이언트와 서버 모두 3-way handshake가 완료되면 이 상태로 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;FIN_WAIT_1
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결을 종료하기 위해 FIN 패킷을 보낸 후 상대방의 ACK를 기다리는 상태입니다.&lt;/li&gt;
&lt;li&gt;연결을 종료하려는 측에서 close()를 호출하면 이 상태로 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;FIN_WAIT_2
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIN 패킷을 보낸 후 상대방의 FIN 패킷을 기다리는 상태입니다.&lt;/li&gt;
&lt;li&gt;FIN_WAIT_1 상태에서 상대방의 ACK를 받으면 이 상태로 전환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CLOSE_WAIT
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상대방으로부터 FIN 패킷을 받고 이를 승인하는 ACK 패킷을 보낸 상태입니다. 이후 연결을 완전히 종료하기 위해 close()를 호출할 때까지 기다립니다.&lt;/li&gt;
&lt;li&gt;상대방이 연결을 종료하려고 할 때, ESTABLISHED 상태에서 FIN 패킷을 받으면 이 상태로 전환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CLOSING
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양쪽 모두 FIN 패킷을 보낸 상태로, 서로의 FIN 패킷에 대한 ACK를 기다립니다.&lt;/li&gt;
&lt;li&gt;양쪽 모두 거의 동시에 연결을 종료하려고 하면 이 상태가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LAST_ACK&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIN 패킷을 보내고 상대방의 ACK 패킷을 기다리는 상태입니다.&lt;/li&gt;
&lt;li&gt;CLOSE_WAIT 상태에서 close()를 호출하면 이 상태로 들어갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;TIME_WAIT&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 패킷이 성공적으로 전달되고 일정 시간(TCP의 경우 2배의 최대 세그먼트 생명주기(MSL)) 동안 대기하는 상태입니다.&lt;/li&gt;
&lt;li&gt;FIN_WAIT_2 상태에서 FIN 패킷을 받고 ACK 패킷을 보낸 후 이 상태로 전환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결(3-way Handshake)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;SYN_SENT (클라이언트) -&amp;gt; SYN_RECEIVED (서버): 클라이언트가 SYN 패킷을 보내고 서버가 이를 받음.&lt;/li&gt;
&lt;li&gt;SYN_RECEIVED (서버) -&amp;gt; SYN_SENT (클라이언트): 서버가 SYN+ACK 패킷을 보내고 클라이언트가 이를 받음.&lt;/li&gt;
&lt;li&gt;ESTABLISHED: 클라이언트가 ACK 패킷을 보내고 서버가 이를 받음.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 종료 (4-way Handshake)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ESTABLISHED -&amp;gt; FIN_WAIT_1 (클라이언트/서버): 연결 종료를 위해 FIN 패킷을 보냄.&lt;/li&gt;
&lt;li&gt;FIN_WAIT_1 -&amp;gt; FIN_WAIT_2 (클라이언트/서버): 상대방의 ACK 패킷을 받음.&lt;/li&gt;
&lt;li&gt;FIN_WAIT_2 -&amp;gt; TIME_WAIT (클라이언트/서버): 상대방의 FIN 패킷을 받고 ACK를 보냄.&lt;/li&gt;
&lt;li&gt;TIME_WAIT -&amp;gt; CLOSED: 일정 시간(MSL의 2배) 후 연결 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;UDP 프로토콜&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lyXbD/btsJtwCbAQL/TrpMJm0eShtZVusiIG9UF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lyXbD/btsJtwCbAQL/TrpMJm0eShtZVusiIG9UF1/img.png&quot; data-alt=&quot;UDP 통신방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lyXbD/btsJtwCbAQL/TrpMJm0eShtZVusiIG9UF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlyXbD%2FbtsJtwCbAQL%2FTrpMJm0eShtZVusiIG9UF1%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;313&quot; height=&quot;284&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UDP 통신방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;UDP는 앞서 말한 데이터그램 타입으로 비연결형 전송방식입니다. TCP와는 달리 데이터 수신여부를 확인하지않아 신뢰성이 낮지만(흐름, 오류제어 x) 속도가 빠르고 네트워크 부하가 적다는 장점이 있습니다. 또한 1:1(유니캐스트), 1:N(멀티캐스트), N:M(브로드캐스트) 통신 방식을 구현하기 좋습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;1012&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FFokp/btsJuB966ao/T11DldUyZ0kkwKUDOgjveK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FFokp/btsJuB966ao/T11DldUyZ0kkwKUDOgjveK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FFokp/btsJuB966ao/T11DldUyZ0kkwKUDOgjveK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFFokp%2FbtsJuB966ao%2FT11DldUyZ0kkwKUDOgjveK%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;851&quot; height=&quot;1012&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;1012&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 비연결형 전송방식이기 때문에 TCP와 다르게 accept하는 과정이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버와 클라이언트 양측에서 소켓을 생성하고 서버에서 소켓을 바인딩하고나면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 메세지를 주고 받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 신뢰성은 떨어지지만 그만큼 전송속도가 빠르기에 실시간 응답이 중요한 채팅이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브 스트리밍 등에 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP / UDP 프로토콜 차이점&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bupDew/btsJuSRhoxb/UNcj0FqzMgCnjqxfowWks1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bupDew/btsJuSRhoxb/UNcj0FqzMgCnjqxfowWks1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bupDew/btsJuSRhoxb/UNcj0FqzMgCnjqxfowWks1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbupDew%2FbtsJuSRhoxb%2FUNcj0FqzMgCnjqxfowWks1%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;1420&quot; height=&quot;771&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>STUDY/C, C++</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/95</guid>
      <comments>https://msh103.tistory.com/95#entry95comment</comments>
      <pubDate>Fri, 6 Sep 2024 23:16:56 +0900</pubDate>
    </item>
    <item>
      <title>C++ 이용해 전화번호부 만들기</title>
      <link>https://msh103.tistory.com/100</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 C++ 문법 공부하면서 계산기 다음으로 전화번호부 만들어보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;[테스트 케이스]&lt;br /&gt;전화번호&amp;nbsp;형식이&amp;nbsp;아니면&amp;nbsp;등록&amp;nbsp;불가 &lt;br /&gt;이름,&amp;nbsp;전화번호&amp;nbsp;일부(4자리)로&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;size14&quot;&gt;&lt;i&gt;&amp;lt;전체 코드&amp;gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722909677303&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt; // 파일 입출력
#include &amp;lt;string&amp;gt; //find를 통해 문자열을 비교하여 조건검색하기 위함

using namespace std;

class BookList {
private:
	int no;
	string name;
	string tel;
public:
	void setStat(int no, string name, string tel) {
		this-&amp;gt;no = no;
		this-&amp;gt;name = name;
		this-&amp;gt;tel = tel;
	}
	int getNo() {
		return no;
	}
	string getName() {
		return name;
	}
	string getTel() {
		return tel;
	}
};

class Menu {
public:
	void mainMenu() {
		cout &amp;lt;&amp;lt; &quot;전화번호부 프로그램입니다.&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;아래 메뉴를 보고 번호를 선택하세요&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1.전화번호부 등록&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;2.전화번호부 조회&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;3.전화번호부 수정&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;4.전화번호부 삭제&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;5.프로그램 종료&quot; &amp;lt;&amp;lt; endl;
	}
	void searchMenu() {
		cout &amp;lt;&amp;lt; &quot;1.이름 검색&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;2.전화번호 검색&quot; &amp;lt;&amp;lt; endl;
	}
};

class Mode {
private:
	BookList index[100];
	Menu menu1;
	int count = 0;
public:
	void addList() {  // 이름, 전화번호 입력 시 순번 순차자동생성
		string name;
		string tel;
		cout &amp;lt;&amp;lt; &quot;전화번호부에 정보를 새로 등록합니다.&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;이름을 입력해주세요&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; name;
		cout &amp;lt;&amp;lt; &quot;전화번호를 입력해주세요. (예 : 010-1234-5678)&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; tel;
		index[count++].setStat(count, name, tel);
	}
	void searchAll() {
		for (int i = 0; i &amp;lt; count; i++) {
			cout &amp;lt;&amp;lt; index[i].getNo() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getName() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getTel() &amp;lt;&amp;lt; endl;
		}
	}
	void searchName() {
		int i = 0;
		string subName;
		//string subTel;
		cout &amp;lt;&amp;lt; &quot;검색할 이름를 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; subName;
		while (i &amp;lt;= 100) {
			if (
				index[i].getName().find(subName) != string::npos) {
				cout &amp;lt;&amp;lt; index[i].getNo() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getName() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getTel() &amp;lt;&amp;lt; endl;
				i++;
				continue;
			}
			i++;
		}
		cout &amp;lt;&amp;lt; &quot;검색이 완료되었습니다.&quot; &amp;lt;&amp;lt; endl;
	}
	void searchTEL() {
		int i = 0;
		string subTel;
		cout &amp;lt;&amp;lt; &quot;검색할 전화번호를 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; subTel;
		while (i &amp;lt;= 100) {
			if (
				index[i].getTel().find(subTel) != string::npos) {
				cout &amp;lt;&amp;lt; index[i].getNo() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getName() &amp;lt;&amp;lt; &quot;|&quot; &amp;lt;&amp;lt; index[i].getTel() &amp;lt;&amp;lt; endl;
				i++;
				continue;
			}
			i++;
		}
		cout &amp;lt;&amp;lt; &quot;검색이 완료되었습니다.&quot; &amp;lt;&amp;lt; endl;
	}
	void searchList() {
		int select;
		cout &amp;lt;&amp;lt; &quot;1.전체 조회 2.조건 검색(이름, 전화번호)&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; select;
		switch (select) {
		case 1: searchAll();
			break;
		case 2: menu1.searchMenu();
			cin &amp;gt;&amp;gt; select;
			switch (select) {
			case 1:searchName();
				break;
			case 2:searchTEL();
				break;
			default:
				cout &amp;lt;&amp;lt; &quot;올바른 숫자를 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
				break;
			}
			break;
		default:
			cout &amp;lt;&amp;lt; &quot;올바른 숫자를 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
			break;
		}
	}// 1. 전체조회 2. 조건 검색
	void updateList() {
		int updateNum;
		string updateName;
		string updateTel;
		searchAll(); // 수정할 목록을 선택하기 위한 전체목록출력
		cout &amp;lt;&amp;lt; &quot;수정할 목록 번호를 입력해주세요&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; updateNum;
		cout &amp;lt;&amp;lt; &quot;이름을 입력해주세요&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; updateName;
		cout &amp;lt;&amp;lt; &quot;전화번호를 입력해주세요. (예 : 010-1234-5678)&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; updateTel;
		index[updateNum - 1].setStat(updateNum, updateName, updateTel);
		cout &amp;lt;&amp;lt; &quot;수정이 완료되었습니다.&quot; &amp;lt;&amp;lt; endl;
	} // 순번 입력 후 이름 전화번호 바꾸면 수정
	void deleteList() {
		int deleteNum;
		searchAll();
		cout &amp;lt;&amp;lt; &quot;삭제할 목록 번호를 입력해주세요&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; deleteNum;
		for (int i = deleteNum - 1; i &amp;lt; count - 1; i++) {
			index[i] = index[i + 1];
		}
		count--; // 항목 개수 감소
		cout &amp;lt;&amp;lt; &quot;삭제가 완료되었습니다.&quot; &amp;lt;&amp;lt; endl;
	} // 순번 입력 시 해당 목록 삭제
};


int main() {
	Mode telbook;
	Menu menu;
	int select;
	while (1) {
		menu.mainMenu();
		cin &amp;gt;&amp;gt; select;
		switch (select) {
		case 1: telbook.addList();
			break;
		case 2: telbook.searchList();
			break;
		case 3: telbook.updateList();
			break;
		case 4: telbook.deleteList();
			break;
		case 5: cout &amp;lt;&amp;lt; &quot;프로그램을 종료합니다&quot; &amp;lt;&amp;lt; endl;
			return 0;
		default:
			cout &amp;lt;&amp;lt; &quot;올바른 번호를 입력해주세요&quot; &amp;lt;&amp;lt; endl;
			break;
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>STUDY/C, C++</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/100</guid>
      <comments>https://msh103.tistory.com/100#entry100comment</comments>
      <pubDate>Fri, 6 Sep 2024 02:41:54 +0900</pubDate>
    </item>
    <item>
      <title>C++을 이용해 계산기 프로그램 만들기</title>
      <link>https://msh103.tistory.com/99</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;새로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; C++을 공부하면서 기초적인 반복문, 조건문 등을 연습하며 새로운 문법에 적응하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 JAVA교육 받을 때 만들어봤던 계산기 기능을 CLI기반으로 간단하게 만들어봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;&amp;lt;전체코드&amp;gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722847932984&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;
class Cal {
public:
	int plus(int result, int y) {
		return result + y;
	};
	int minus(int result, int y) {
		return result -= y;
	};
	int multiple(int result, int y) {
		return result *= y;
	};
	int division(int result, int y) {
		if (y == 0) {
			cout &amp;lt;&amp;lt; &quot;나눌 수 없는 숫자 입니다.&quot; &amp;lt;&amp;lt; endl;
			return result;
		}
		return result /= y;
	};
};

int main() {
	Cal Calcul;
	int result = 0;
	int select = 0;
	int b;
	cout &amp;lt;&amp;lt; &quot;숫자를 입력해주세요&quot; &amp;lt;&amp;lt; endl;
	cin &amp;gt;&amp;gt; result;
	while (1) {
		cout &amp;lt;&amp;lt; result &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1.더하기&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;2.빼기&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;3.곱하기&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;4.나누기&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;5.초기화&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;6.종료&quot; &amp;lt;&amp;lt; endl;
		cin &amp;gt;&amp;gt; select;

		switch (select)
		{
		case 1:
			cout &amp;lt;&amp;lt; &quot;더하기 숫자를 입력해주세요.&quot;;
			cin &amp;gt;&amp;gt; b;
			result = Calcul.plus(result, b);
			break;
		case 2:
			cout &amp;lt;&amp;lt; &quot;빼기 숫자를 입력해주세요.&quot;;
			cin &amp;gt;&amp;gt; b;
			result = Calcul.minus(result, b);
			break;
		case 3:
			cout &amp;lt;&amp;lt; &quot;곱하기 숫자를 입력해주세요.&quot;;
			cin &amp;gt;&amp;gt; b;
			result = Calcul.multiple(result, b);
			break;
		case 4:
			cout &amp;lt;&amp;lt; &quot;나누기 숫자를 입력해주세요.&quot;;
			cin &amp;gt;&amp;gt; b;
			result = Calcul.division(result, b);
			break;
		case 5: cout &amp;lt;&amp;lt; &quot;초기화&quot; &amp;lt;&amp;lt; endl;
			result = 0;
			break;
		case 6: 
			return 0;
			break;
		default:
			cout &amp;lt;&amp;lt; &quot;잘못된 선택입니다. 다시 시도하세요.&quot; &amp;lt;&amp;lt; endl;
			break;
		}
	}
	return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>STUDY/C, C++</category>
      <author>MSH103</author>
      <guid isPermaLink="true">https://msh103.tistory.com/99</guid>
      <comments>https://msh103.tistory.com/99#entry99comment</comments>
      <pubDate>Fri, 6 Sep 2024 02:41:40 +0900</pubDate>
    </item>
  </channel>
</rss>