<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Rootable의 개발일기</title>
    <link>https://dev-rootable.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 07:59:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>dev-rootable</managingEditor>
    <item>
      <title>HTTP 요청과 응답을 다루는 클래스</title>
      <link>https://dev-rootable.tistory.com/167</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7sOvB/dJMb9LDZbOt/czKQN0IQa7Xt0PMYX4qb21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7sOvB/dJMb9LDZbOt/czKQN0IQa7Xt0PMYX4qb21/img.png&quot; data-alt=&quot;References: Google Gemini&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7sOvB/dJMb9LDZbOt/czKQN0IQa7Xt0PMYX4qb21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7sOvB%2FdJMb9LDZbOt%2FczKQN0IQa7Xt0PMYX4qb21%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;607&quot; height=&quot;607&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;References: Google Gemini&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;스프링에서 HTTP 요청과 응답을 다루는 대표적인 클래스로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;InputStream/OutputStream&lt;/b&gt;: 바이너리 데이터를 직접 다루는 가장 낮은 레벨의 추상화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@RequestBody/@ResponseBody&lt;/b&gt;: 문자열(JSON/XML)을 객체로 자동 변환하는 중간 레벨의 추상화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HttpEntity&lt;/b&gt;: HTTP 헤더, 상태 코드, 바디를 모두 포함하는 메시지 전체를 다루는 가장 높은 레벨의 추상화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  InputStream/OutputStream (원시 스트림)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;java.io의 클래스로 HTTP Body의 데이터를 원시 바이트(byte) 스트림으로 다룸&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;처리 방식(스트리밍)&lt;/b&gt;: 데이터를 &lt;u&gt;&lt;b&gt;청크(chunk) 단위&lt;/b&gt;&lt;/u&gt;로 조금씩 읽고 씀(&lt;u&gt;전체 데이터를 메모리에 올리지 않음&lt;/u&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 변환&lt;/b&gt;: 전혀 없음, 개발자가 직접 바이트를 읽어 파일로 저장하거나 파일에서 바이트를 읽어 응답으로 쏴줘야 함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주 사용처&lt;/b&gt;: 대용량 파일 업로드 및 다운로드 시 메모리 초과(OutOfMemoryError)를 방지하기 위함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  @RequestBody/@ResponseBody (자동 변환 애노테이션)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;메서드 파라미터나 반환 값에 붙이는 Annotation&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;처리 방식(버퍼링)&lt;/b&gt;: HTTP Body의 내용을 한 번에 전부 읽어서 메모리에 올린(&lt;b&gt;&lt;u&gt;버퍼링&lt;/u&gt;&lt;/b&gt;) 후 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 변환&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;@RequestBody: 요청 body의 JSON/XML ➡ Java 객체 (HttpMessageConverter, 주로 Jackson)&lt;/li&gt;
&lt;li&gt;@ResponseBody: 메서드가 반환하는 Java 객체 ➡ JSON/XML&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주 사용처&lt;/b&gt;: 대부분의 REST API에서 JSON 데이터를 객체로 주고 받을 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  HttpEntity/ResponseEntity (HTTP 메시지 전체)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;HTTP 요청/응답 전체를 감싸는 클래스(객체)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;처리 방식(버퍼링)&lt;/b&gt;: HTTP Body의 내용을 한 번에 전부 읽어서 메모리에 올린(&lt;b&gt;&lt;u&gt;버퍼링&lt;/u&gt;&lt;/b&gt;) 후 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HttpEntity&amp;lt;T&amp;gt;: HTTP 헤더와 바디를 함께 다룰 때 사용&lt;/li&gt;
&lt;li&gt;ResponseEntity&amp;lt;T&amp;gt;: HttpEntity를 상속 받아, 응답 상태 코드, 헤더, 바디를 모두 제어 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 변환&lt;/b&gt;: T 제네릭 타입에 대해 @RequestBody/@ResponseBody와 동일하게 자동 변환을 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주 사용처&lt;/b&gt;: HTTP 상태 코드를 명시적으로 반환하거나, 응답에 커스텀 헤더를 추가해야 할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/167</guid>
      <comments>https://dev-rootable.tistory.com/167#entry167comment</comments>
      <pubDate>Tue, 21 Oct 2025 16:56:39 +0900</pubDate>
    </item>
    <item>
      <title>퀵 정렬(Quick Sort)</title>
      <link>https://dev-rootable.tistory.com/165</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  퀵 정렬이란&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 리스트를 피벗(pivot)을 기준으로 하나는 pivot보다 작은 값들의 부분리스트, 하나는 pivot보다 큰 값들의 부분리스트로 정렬한 뒤, 각 부분리스트에 대해 재귀적으로 정렬하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  분할 정복(Divide and Conquer) 알고리즘&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;퀵 정렬은 기본적으로 &lt;b&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;'분할 정복'&lt;/span&gt;&lt;/b&gt; 알고리즘을 기반으로 정렬되는 방식이다. 다만, 병합 정렬(Merge Sort)과 다른 점은 병합 정렬은 하나의 리스트를 &lt;b&gt;'절반' &lt;/b&gt;으로 나누어 분할 정복을 하고, 퀵 정렬은 &lt;b&gt;피벗(pivot)&lt;/b&gt;을 기준으로 분할 정복을 하기 때문에 하나의 리스트에 대해 비균등하게 나뉠 수 있다는 점이다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;일반적인 성능: 퀵 정렬 &amp;gt; 병합 정렬&lt;/b&gt;&lt;/blockquote&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;b&gt;'제자리 정렬(in-place sort)'&lt;/b&gt; 이라고도 한다. 또한, 두 개의 부분 리스트를 만들 때 서로 떨어진 원소끼리 교환이 일어나므로 &lt;b&gt;'불안정정렬(Unstable Sort)'&lt;/b&gt; 알고리즘이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  정렬 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;피벗 선택&lt;/li&gt;
&lt;li&gt;피벗을 기준으로 왼쪽은 피벗보다 큰 값, 오른쪽은 피벗보다 작은 값을 탐색&lt;/li&gt;
&lt;li&gt;양 방향에서 찾은 두 원소를 교환&lt;/li&gt;
&lt;li&gt;왼쪽에서 탐색하는 위치와 오른쪽에서 탐색하는 위치가 엇갈리지 않을 때까지 2번으로 돌아가 위 과정을 반복&lt;/li&gt;
&lt;li&gt;엇갈린 기점을 기준으로 두 개의 부분 리스트로 나누어 1번으로 돌아가 해당 부분 리스트의 길이가 1이 아닐 때까지 1번 과정을 반복(Divide: 분할)&lt;/li&gt;
&lt;li&gt;인접한 부분리스틑끼리 병합 (Conqure: 정복)&lt;/li&gt;
&lt;/ol&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;b&gt;(Lomuto 방식)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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-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;713&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGrvCW/btsQTNnTyPB/qf6ycD49yIuWnjdpOOXaEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGrvCW/btsQTNnTyPB/qf6ycD49yIuWnjdpOOXaEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGrvCW/btsQTNnTyPB/qf6ycD49yIuWnjdpOOXaEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGrvCW%2FbtsQTNnTyPB%2Fqf6ycD49yIuWnjdpOOXaEk%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;713&quot; height=&quot;693&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;686&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd1ALb/btsQSwHdJCf/6X2mSsxrFqILaYibzRMEKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd1ALb/btsQSwHdJCf/6X2mSsxrFqILaYibzRMEKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd1ALb/btsQSwHdJCf/6X2mSsxrFqILaYibzRMEKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd1ALb%2FbtsQSwHdJCf%2F6X2mSsxrFqILaYibzRMEKk%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;686&quot; height=&quot;724&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;876&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhV7EI/btsQSApfHHi/zL6umeQuhMB9wLO0lCW4u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhV7EI/btsQSApfHHi/zL6umeQuhMB9wLO0lCW4u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhV7EI/btsQSApfHHi/zL6umeQuhMB9wLO0lCW4u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhV7EI%2FbtsQSApfHHi%2FzL6umeQuhMB9wLO0lCW4u0%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;876&quot; height=&quot;750&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;765&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v1TwE/btsQTSW11dP/saO0dPZPkJp9kWepB3rgpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v1TwE/btsQTSW11dP/saO0dPZPkJp9kWepB3rgpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v1TwE/btsQTSW11dP/saO0dPZPkJp9kWepB3rgpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv1TwE%2FbtsQTSW11dP%2FsaO0dPZPkJp9kWepB3rgpk%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;765&quot; height=&quot;510&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;742&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tOAVB/btsQSvuJW4b/PKTKKyPHeaMmM7U5ZPUef1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tOAVB/btsQSvuJW4b/PKTKKyPHeaMmM7U5ZPUef1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tOAVB/btsQSvuJW4b/PKTKKyPHeaMmM7U5ZPUef1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtOAVB%2FbtsQSvuJW4b%2FPKTKKyPHeaMmM7U5ZPUef1%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;742&quot; height=&quot;507&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;Ref:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://st-lab.tistory.com/250&quot;&gt;https://st-lab.tistory.com/250&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745222669154&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class QuickSort {

    public static void sort(int[] A) {
        l_pivot_sort(A, 0, A.length - 1);
    }

    public static void l_pivot_sort(int[] A, int lo, int hi) {
        if (lo &amp;gt;= hi) return;

        int pivot = partition(A, lo, hi);

        l_pivot_sort(A, lo, pivot - 1); //왼쪽 부분 리스트 정복
        l_pivot_sort(A, pivot + 1, hi); //오른쪽 부분 리스트 정복
    }

    public static int partition(int[] A, int left, int right) {
        int lo = left;
        int hi = right;
        int pivot = A[left];

        while (lo &amp;lt; hi) {
            while (A[hi] &amp;gt; pivot &amp;amp;&amp;amp; lo &amp;lt; hi) hi--; //우측부터 pivot 보다 작은 값 찾기
            while (A[lo] &amp;lt;= pivot &amp;amp;&amp;amp; lo &amp;lt; hi) lo++; //좌측부터 pivot 보다 큰 값 찾기
            swap(A, lo, hi);
        }

        swap(A, left, lo);
        return lo;
    }

    public static void swap(int[] A, int i, int j) {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }

}&lt;/code&gt;&lt;/pre&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;  오른쪽 피벗 선택 방식 (Lomuto 방식)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd5boE/btsQSrznMy5/Wn3hbPTkGgkrCsudQrjgXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd5boE/btsQSrznMy5/Wn3hbPTkGgkrCsudQrjgXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd5boE/btsQSrznMy5/Wn3hbPTkGgkrCsudQrjgXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd5boE%2FbtsQSrznMy5%2FWn3hbPTkGgkrCsudQrjgXK%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;752&quot; height=&quot;704&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;704&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;696&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XH1Zv/btsQUjz47Ru/SjVVofeAKCMbIfOKnrsyjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XH1Zv/btsQUjz47Ru/SjVVofeAKCMbIfOKnrsyjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XH1Zv/btsQUjz47Ru/SjVVofeAKCMbIfOKnrsyjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXH1Zv%2FbtsQUjz47Ru%2FSjVVofeAKCMbIfOKnrsyjK%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;696&quot; height=&quot;716&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;822&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBh7s9/btsQQ0P7hDo/leDtQAlNBSavKD0pya4rJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBh7s9/btsQQ0P7hDo/leDtQAlNBSavKD0pya4rJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBh7s9/btsQQ0P7hDo/leDtQAlNBSavKD0pya4rJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBh7s9%2FbtsQQ0P7hDo%2FleDtQAlNBSavKD0pya4rJ1%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;822&quot; height=&quot;725&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;544&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAq7KD/btsQQDt23D3/Au8JB0tce8piO3YO2ScXgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAq7KD/btsQQDt23D3/Au8JB0tce8piO3YO2ScXgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAq7KD/btsQQDt23D3/Au8JB0tce8piO3YO2ScXgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAq7KD%2FbtsQQDt23D3%2FAu8JB0tce8piO3YO2ScXgK%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;569&quot; height=&quot;818&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;Ref:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://st-lab.tistory.com/250&quot;&gt;https://st-lab.tistory.com/250&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745223264664&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class QuickSort {

    public static void sort(int[] A) {
        r_pivot_sort(A, 0, A.length - 1);
    }

    public static void r_pivot_sort(int[] A, int lo, int hi) {
        if (lo &amp;gt;= hi) return;

        int pivot = partition(A, lo, hi);

        r_pivot_sort(A, lo, pivot - 1); //왼쪽 부분 리스트 정복
        r_pivot_sort(A, pivot + 1, hi); //오른쪽 부분 리스트 정복
    }

    public static int partition(int[] A, int left, int right) {
        int lo = left;
        int hi = right;
        int pivot = A[right];

        while (lo &amp;lt; hi) {
            while (A[lo] &amp;lt; pivot &amp;amp;&amp;amp; lo &amp;lt; hi) lo++; //좌측부터 pivot 보다 큰 값 찾기
            while (A[hi] &amp;gt;= pivot &amp;amp;&amp;amp; lo &amp;lt; hi) hi--; //우측부터 pivot 보다 작은 값 찾기
            swap(A, lo, hi);
        }

        swap(A, right, hi);
        return hi;
    }

    public static void swap(int[] A, int i, int j) {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }

}&lt;/code&gt;&lt;/pre&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;  중간 피벗 선택 방식 (Hoare 방식)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 이미 피벗 기준으로 양쪽이 분할돼 있으므로, 피벗을 다시 옮길 필요 없다.&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;➡ 마지막에 pivot을 swap하지 않으므로, 두 포인터가 같아질 때 바로 swap 후 종료&lt;/span&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUDBgU/btsQSHorPL0/jBZJKOzkXwVceWJBTFRfk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUDBgU/btsQSHorPL0/jBZJKOzkXwVceWJBTFRfk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUDBgU/btsQSHorPL0/jBZJKOzkXwVceWJBTFRfk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUDBgU%2FbtsQSHorPL0%2FjBZJKOzkXwVceWJBTFRfk0%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;757&quot; height=&quot;700&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;728&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b70aJs/btsQRFea9mw/HkEifGrLxVgaSQVrwQC2jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b70aJs/btsQRFea9mw/HkEifGrLxVgaSQVrwQC2jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b70aJs/btsQRFea9mw/HkEifGrLxVgaSQVrwQC2jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb70aJs%2FbtsQRFea9mw%2FHkEifGrLxVgaSQVrwQC2jk%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;728&quot; height=&quot;722&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;858&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cViTMc/btsQRG4Caf3/EhrWOyzjbvwbG2vZ2c43h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cViTMc/btsQRG4Caf3/EhrWOyzjbvwbG2vZ2c43h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cViTMc/btsQRG4Caf3/EhrWOyzjbvwbG2vZ2c43h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcViTMc%2FbtsQRG4Caf3%2FEhrWOyzjbvwbG2vZ2c43h1%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;858&quot; height=&quot;364&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;764&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wiGaP/btsQSPzKf9z/AZvVuYTy79BrM61kj5vpb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wiGaP/btsQSPzKf9z/AZvVuYTy79BrM61kj5vpb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wiGaP/btsQSPzKf9z/AZvVuYTy79BrM61kj5vpb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwiGaP%2FbtsQSPzKf9z%2FAZvVuYTy79BrM61kj5vpb1%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;685&quot; height=&quot;581&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;641&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtEz58/btsQQYY4BTN/NJ8fGv3XikEMwvPRSX76E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtEz58/btsQQYY4BTN/NJ8fGv3XikEMwvPRSX76E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtEz58/btsQQYY4BTN/NJ8fGv3XikEMwvPRSX76E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtEz58%2FbtsQQYY4BTN%2FNJ8fGv3XikEMwvPRSX76E1%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;682&quot; height=&quot;685&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;669&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c46FGS/btsQRDm6CfY/UjzXZ3N91ukQO586NzBkA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c46FGS/btsQRDm6CfY/UjzXZ3N91ukQO586NzBkA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c46FGS/btsQRDm6CfY/UjzXZ3N91ukQO586NzBkA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc46FGS%2FbtsQRDm6CfY%2FUjzXZ3N91ukQO586NzBkA1%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;669&quot; height=&quot;504&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p 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;630&quot; data-origin-height=&quot;739&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBcA9J/btsQS80jS28/hZ7EWFmoLOGi30Sp1m8e70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBcA9J/btsQS80jS28/hZ7EWFmoLOGi30Sp1m8e70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBcA9J/btsQS80jS28/hZ7EWFmoLOGi30Sp1m8e70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBcA9J%2FbtsQS80jS28%2FhZ7EWFmoLOGi30Sp1m8e70%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;630&quot; height=&quot;739&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;739&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Ref: &lt;a href=&quot;https://st-lab.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://st-lab.tistory.com/250&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745224671329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class QuickSort {

    public static void sort(int[] A) {
        m_pivot_sort(A, 0, A.length - 1);
    }

    public static void m_pivot_sort(int[] A, int lo, int hi) {
        if (lo &amp;gt;= hi) return;

        int pivot = partition(A, lo, hi);

        m_pivot_sort(A, lo, pivot - 1); //왼쪽 부분 리스트 정복
        m_pivot_sort(A, pivot + 1, hi); //오른쪽 부분 리스트 정복
    }

    public static int partition(int[] A, int left, int right) {
        int lo = left;
        int hi = right;
        int pivot = A[(left + right) / 2];

        while (lo &amp;lt;= hi) {
            while (A[lo] &amp;lt; pivot) lo++;
            while (A[hi] &amp;gt; pivot) hi--;

            if (lo &amp;lt;= hi) {
                swap(A, lo, hi);
                lo++;
                hi--;
            }
        }

        return hi;
    }

    public static void swap(int[] A, int i, int j) {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  시간 복잡도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;O(NlogN)의 시간복잡도&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKD9gM/btsQRxUqiwv/QW6PalfvSHyoZguWKotsZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKD9gM/btsQRxUqiwv/QW6PalfvSHyoZguWKotsZ0/img.png&quot; data-alt=&quot;Ref: https://st-lab.tistory.com/250&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKD9gM/btsQRxUqiwv/QW6PalfvSHyoZguWKotsZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKD9gM%2FbtsQRxUqiwv%2FQW6PalfvSHyoZguWKotsZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;476&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ref: https://st-lab.tistory.com/250&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 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이상적으로 피벗이 중앙에 위치되어 리스트를 절반으로 나눈다고 할 때, N개의 데이터가 있는 리스트를 1개까지 쪼개어 트리로 나타내게 되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Binary Tree&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;형태로 나온다. 알고리즘에서 피벗을 잡고 큰 요소와 작은 요소를 각 끝단에서 시작하여 탐색하며 만족하지 못하는 경우 swap 한다. 이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;현재 리스트의 요소들을 탐색하기 때문에 O(N)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;좀 더 자세하게 말하자면 i번째 레벨에서 노드의 개수가 2^i개이고, 노드의 크기(=한 노드에 들어있는 원소의 개수)는 N/2^i개다. 이를 곱하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;한 레벨에서 비교 작업에 대한 시간복잡도는 O(2^i * N/2^i)이고 곧 O(N)&lt;/b&gt;이다. 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;O(N)의 비교 작업을 트리의 높이인 logN-1번 수행&lt;/b&gt;하므로 &lt;span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;O(NlogN)의 시간복잡도&lt;/b&gt;&lt;/span&gt;를 갖는 것을 알 수 있다. &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;퀵 정렬은 정렬된 상태의 배열을 정렬하고자 할 때 최악의 성능을 보인다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;적은 오버헤드와 참조 지역성(캐시 효율성)&lt;/blockquote&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;1. 상수 요인(Constant Factor) 이 작다. (적은 오버헤드)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;빅오(Big-O) 표기법은 데이터의 양(N)이 커질 때의 성장률을 나타낼 뿐, 실제 연산 횟수에 곱해지는 상수는 무시한다. 퀵 정렬의 핵심 연산(inner loop)은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;단순한 비교와 값 교환(swap)으로만 이루어져 있어 매우 가볍다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;퀵 정렬&lt;/b&gt;: while (arr[i] &amp;lt; pivot) 처럼 단순 비교만 반복&lt;/li&gt;
&lt;li&gt;&lt;b&gt;병합 정렬&lt;/b&gt;: 정렬된 두 배열을 합치기 위해 추가적인 메모리 공간을 할당하고, 데이터를 복사하는 과정에서 상대적으로 무거운 작업(오버 헤드)이 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;즉, 같은 O(NlogN) 이라도 실제 기계가 실행하는 명령어의 수가 퀵 정렬이 훨씬 적기 때문에 더 빠르다.&lt;/b&gt;&lt;/u&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;b&gt;2. 참조 지역성(Locality of Reference) 원리를 잘 활용한다. (캐시 효율성)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 메모리(RAM)에서 데이터를 가져올 때, 속도가 훨씬 빠른 캐시 메모리에 해당 데이터와 그 주변 데이터를 함께 저장한다. 그 후, 필요한 데이터가 캐시에 있으면(Cache Hit) 메모리까지 가지 않고 매우 빠르게 작업을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;퀵 정렬&lt;/b&gt;: 배열의 특정 구간(파티션) 내에서 순차적으로 데이터를 접근하며 정렬 ➡ 한번 캐시에 올라온 데이터를 계속 활용할 가능성이 높아 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;캐시 히트율(Cache Hit Ratio)이 매우 높음&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다른 정렬 방식&lt;/b&gt;: 데이터 접근이 상대적으로 여기저기 흩어져 있어 캐시 효율성이 떨어질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;퀵 정렬은 한 페이지에 있는 글자들을 순서대로 읽는 것, 병합 정렬은 책의 10페이지와 100페이지를 번갈아 보는 것&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  Hoare vs Lomuto 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;pivot의 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;처리 방식&lt;/span&gt;과 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;분할 효율성&lt;/span&gt; 차이&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;Lomuto 방식&lt;/span&gt;&lt;/b&gt;은 피봇을 정하고 그보다 작은 값을 왼쪽으로, 큰 값을 오른쪽으로 보내 피봇의 최종 위치를 정합니다. 이 방식은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;직관적&lt;/b&gt;&lt;/span&gt;이지만, 이미 정렬된 배열 같은 특정 경우에 성능이 크게 저하될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;Hoare 방식&lt;/b&gt;&lt;/span&gt;은 배열 양 쪽 끝에서부터 탐색하여 피봇보다 크거나 작은 값을 교환해 나간다. 이 방식은 최종 위치가 정해지진 않지만, 일반적으로 Lomuto 방식보다 비교와 교환 횟수가 적어 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;더 빠르고 효율적&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  정리&lt;/b&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 복잡도가 O(NlogN) 이다. 다른 O(NlogN) 정렬 알고리즘 대비 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;오버헤드가 적고 캐시 효율성(참조 지역성)&lt;/b&gt;&lt;/span&gt;이 높아 실제 실행 속도가 가장 빠른 정렬 알고리즘 중 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  제자리 정렬(In-place sort)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬을 위한 별도의 추가 메모리가 필요하지 않음. 이는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;대용량 데이터를 정렬할 때 큰 장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  최악의 경우 성능 저하(O(N^2))&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pivot 이 한 쪽으로 치우치게 선택될 경우 (이미 정렬된 배열) 시간 복잡도가 O(N^2)으로 성능이 급격히 나빠질 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  불안정 정렬(Unstable sort)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 같은 데이터들의 기존 순서가 정렬 후에도 유지되지 않을 수 있음. 따라서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;데이터의 기존 순서가 중요한 경우에는 사용하기 부적합.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;References:&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;a href=&quot;https://st-lab.tistory.com/250&quot;&gt;https://st-lab.tistory.com/250&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1758959988173&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;자바 [JAVA] - 퀵 정렬 (Quick Sort)&quot; data-og-description=&quot;[정렬 알고리즘 모음] 더보기 1. 계수 정렬 (Counting Sort) 2. 선택 정렬 (Selection Sort) 3. 삽입 정렬 (Insertion Sort) 4. 거품 정렬 (Bubble Sort) 5. 셸 정렬 (Shell Sort) 6. 힙 정렬 (Heap Sort) 7. 합병(병합) 정렬 (Merge &quot; data-og-host=&quot;st-lab.tistory.com&quot; data-og-source-url=&quot;https://st-lab.tistory.com/250&quot; data-og-url=&quot;https://st-lab.tistory.com/250&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QFbkT/hyZJWcjbeY/5NKi4QKtrMG0lHrPs7KDiK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/clhvkh/hyZJXWAyGd/ekgjdtRpVARi2ZvW7UH3zk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fSn1H/hyZJ4OWOQB/D41qnzQBlRkIC0dKMpGL81/img.png?width=4500&amp;amp;height=4900&amp;amp;face=0_0_4500_4900&quot;&gt;&lt;a href=&quot;https://st-lab.tistory.com/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://st-lab.tistory.com/250&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QFbkT/hyZJWcjbeY/5NKi4QKtrMG0lHrPs7KDiK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/clhvkh/hyZJXWAyGd/ekgjdtRpVARi2ZvW7UH3zk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/fSn1H/hyZJ4OWOQB/D41qnzQBlRkIC0dKMpGL81/img.png?width=4500&amp;amp;height=4900&amp;amp;face=0_0_4500_4900');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;자바 [JAVA] - 퀵 정렬 (Quick Sort)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[정렬 알고리즘 모음] 더보기 1. 계수 정렬 (Counting Sort) 2. 선택 정렬 (Selection Sort) 3. 삽입 정렬 (Insertion Sort) 4. 거품 정렬 (Bubble Sort) 5. 셸 정렬 (Shell Sort) 6. 힙 정렬 (Heap Sort) 7. 합병(병합) 정렬 (Merge&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;st-lab.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/165</guid>
      <comments>https://dev-rootable.tistory.com/165#entry165comment</comments>
      <pubDate>Mon, 21 Apr 2025 14:40:41 +0900</pubDate>
    </item>
    <item>
      <title>단체 채팅 프로그램 개발</title>
      <link>https://dev-rootable.tistory.com/164</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pexels-anton-8100-46924.jpg&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbaURe/btsIGZ5pVKi/A2hO9hwPH1KibD4Gk4Hhkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbaURe/btsIGZ5pVKi/A2hO9hwPH1KibD4Gk4Hhkk/img.jpg&quot; data-alt=&quot;Ref: https://www.pexels.com/ko-kr/photo/whatsapp-46924/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbaURe/btsIGZ5pVKi/A2hO9hwPH1KibD4Gk4Hhkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbaURe%2FbtsIGZ5pVKi%2FA2hO9hwPH1KibD4Gk4Hhkk%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;640&quot; height=&quot;427&quot; data-filename=&quot;pexels-anton-8100-46924.jpg&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ref: https://www.pexels.com/ko-kr/photo/whatsapp-46924/&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;Ubuntu 환경에서 단체 채팅방을 C와 MySQL을 통해 구현했다. MySQL은 채팅 내용을 저장하는 용도로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  기능 목록&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 사용자에게 채팅 메시지가 전달된다.&lt;/li&gt;
&lt;li&gt;최초 접속하면 최근 10개의 채팅을 볼 수 있다.&lt;/li&gt;
&lt;li&gt;채팅 내용은 MySQL에 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  클라이언트 동작 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;MySQL 커넥션 초기화 및 연결&lt;/li&gt;
&lt;li&gt;서버와 통신할 소켓 생성&lt;/li&gt;
&lt;li&gt;소켓에 연결할 서버 주소 정보 바인딩&lt;/li&gt;
&lt;li&gt;서버에 연결 요청&lt;/li&gt;
&lt;li&gt;DB로부터 최근 10개 레코드 조회 후 각 클라이언트 화면에 출력&lt;/li&gt;
&lt;li&gt;데이터 전송
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시간 정보, 사용자 이름, 채팅 메시지 등을 포맷팅 하여 서버에 전송&lt;/li&gt;
&lt;li&gt;클라이언트가 'exit' 명령을 입력하면 로그아웃으로 간주하여 close 수행&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;데이터 수신
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버에서 작성한 메시지가 있다면 읽어온 후 출력&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  서버 동작 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;MySQL 커넥션 초기화 및 연결&lt;/li&gt;
&lt;li&gt;듣기 소켓 생성&lt;/li&gt;
&lt;li&gt;듣기 소켓에 서버 주소 정보 바인딩&lt;/li&gt;
&lt;li&gt;클라이언트 요청 대기&lt;/li&gt;
&lt;li&gt;Connect를 시도한 클라이언트 요청을 받으면 클라이언트 소켓 생성&lt;/li&gt;
&lt;li&gt;클라이언트가 전송한 메시지를 모든 클라이언트에게 보낸 후 저장&lt;/li&gt;
&lt;li&gt;접속한 클라이언트 IP와 Port 등 주소 정보 출력&lt;/li&gt;
&lt;li&gt;접속한 클라이언트에게 로그인 메시지 전달&lt;/li&gt;
&lt;li&gt;채팅 데이터를 DB에 INSERT 쿼리&lt;/li&gt;
&lt;li&gt;종료를 요청한 클라이언트 소켓 제거&lt;/li&gt;
&lt;li&gt;서버 소켓 종료 및 MySQL 연결 해제&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  들어가기 전 알아두기&lt;/b&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vPWzU/btsIGjjiAyc/SvVIpwHEU5kvKtyA0i9vCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vPWzU/btsIGjjiAyc/SvVIpwHEU5kvKtyA0i9vCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vPWzU/btsIGjjiAyc/SvVIpwHEU5kvKtyA0i9vCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvPWzU%2FbtsIGjjiAyc%2FSvVIpwHEU5kvKtyA0i9vCK%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;432&quot; height=&quot;498&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;❓ 소켓을 어떻게 접근할까&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;socket descriptor&lt;/b&gt;&lt;/span&gt; 라는 식별자를 통해 접근할 수 있다. 해당 값은 int 형으로 &lt;b&gt;socket() 함수를 통해 소켓을 생성하면 반환 값&lt;/b&gt;으로 얻을 수 있다. 이는 UNIX에서 파일 open 후 file descriptor를 얻는 것과 같은 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;❓ 소켓 바인딩하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 프로그래밍에서는 바인딩을 위해 생성자를 사용하고, 그 생성자의 본체가 클래스다. 소켓은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;sockaddr이라는&lt;/b&gt;&lt;/span&gt; 구조체가 본체이고, &lt;b&gt;해당 구조체에 바인딩하기 위해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;sockaddr_in&lt;/span&gt; 구조체를 사용&lt;/b&gt;한다. 그 이유는 &lt;u&gt;&lt;b&gt;sockaddr 구조체 내에 주소를 담는 char 배열이 있는데 이것을 좀 더 세분화해서 나눈 것이 sockaddr_in&lt;/b&gt;&lt;/u&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;sockaddr 구조체는 &lt;b&gt;리눅스/유닉스 시스템일 경우 &amp;lt;sys/socket.h&amp;gt; 헤더 파일, 윈도우는 &amp;lt;winsock.h&amp;gt; 헤더 파일&lt;/b&gt;을 통해 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;sys/socket.h: 리눅스/유닉스 시스템에서 소켓의 주소를 저장하거나 표현하는 데 사용하는 구조체를 제공&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sockaddr_in 구조체 내에 주소 체계, 주소, 포트 등의 멤버에 값을 입력하고,&amp;nbsp;socket 식별자에 바인딩하면 된다. 이 작업을 위한 bind() 함수는 아래와 같이 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721542028990&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int bind(int sockfd, struct sockaddr *addr, int addr_len)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;주소를 바인딩하기 위해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;arpa/inet.h&lt;/span&gt; 라는 헤더 파일이 필요&lt;/b&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;arpa/inet.h: 숫자로 IP 주소를 조작하는 기능들을 정의한 헤더 파일&lt;/b&gt;&lt;/blockquote&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;span style=&quot;background-color: #ffffff; color: #3d4444; text-align: left;&quot;&gt;&lt;b&gt;htonl(), htons(), ntohl(), ntohs()&lt;/b&gt; 등의 함수가 있는데 이들은 &lt;u&gt;&lt;b&gt;소켓을 통해 다른 기종 간에 데이터를 송수신할 수 있도록 데이터를 변환&lt;/b&gt;&lt;/u&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;❓ 소켓 통신에서 주소를 변환하는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Big Endian과 Little Endian&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;소켓으로 통신을 할 때 TCP/IP와 호스트는 Byte를 저장하는 순서가 다르다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;저장할 때 상위 바이트, 즉 큰 쪽을 먼저 저장하는 것&lt;/b&gt;을 &lt;b&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;빅 엔디안(Big Endian)&lt;/span&gt;&lt;/b&gt;,&lt;br /&gt;&lt;b&gt;저장할 때 하위 바이트, 즉 작은 쪽을 먼저 저장하는 것&lt;/b&gt;을 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;리틀 엔디안(Little Endian)&lt;/b&gt;&lt;/span&gt;이라고 한다.&lt;br /&gt;&lt;br /&gt;여기서 먼저 저장한다는 것은 앞 주소에 둔다는 것을 의미한다.&lt;br /&gt;&lt;br /&gt;예를 들어, 컴퓨터에 int형 4 byte 데이터 0x010203040을 저장한다고 했을 때, 다음과 같이 저장한다.&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCjXyR/btsIyCVIiot/sJUlZ8Zv4ZAvT9wTGOP7Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCjXyR/btsIyCVIiot/sJUlZ8Zv4ZAvT9wTGOP7Yk/img.png&quot; data-alt=&quot;출처: https://softtone-someday.tistory.com/20&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCjXyR/btsIyCVIiot/sJUlZ8Zv4ZAvT9wTGOP7Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCjXyR%2FbtsIyCVIiot%2FsJUlZ8Zv4ZAvT9wTGOP7Yk%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;450&quot; height=&quot;316&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://softtone-someday.tistory.com/20&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;가령 TCP/IP에 주소 정보를 전송한다고 했을 때, &lt;b&gt;네트워크에서는 빅 엔디안으로 통일&lt;/b&gt;하기 때문에 &lt;u&gt;&lt;b&gt;&amp;lt;arpa/inet.h&amp;gt;와 같은 헤더 파일의 함수를 통해 별도 처리하여 전송해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;/blockquote&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;❓ accept() 함수를 수행한 후 소켓이 다시 반환되는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 listen() 함수를 통해 클라이언트의 연결 요청을 대기한다. 클라이언트 요청 정보는 시스템 내부적으로 관리되는 큐에 쌓이게 되는데, &lt;b&gt;큐에 대기하고 있는 요청을 꺼내와서 연결을 완료하는 작업을 accept() 함수가 수행&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;듣기 소켓&lt;/b&gt;&lt;/span&gt;과 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;연결 소켓&lt;/b&gt;&lt;/span&gt;이라는 개념을 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;socket() 함수를 통해 생성된 소켓은 연결 요청을 확인하는 또는 듣는 역할만 수행&lt;/b&gt;한다. 그래서 연결 요청을 받으면 즉시 수신 대기열로 넘겨 다음 요청을 대기한다. 그렇다면 accept() 함수를 통해 반환된 소켓은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNqkWD/btsIGxhgfan/SLjq6AMRSOG8fblrpc9Ul0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNqkWD/btsIGxhgfan/SLjq6AMRSOG8fblrpc9Ul0/img.png&quot; data-alt=&quot;출처: https://codingfarm.tistory.com/538&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNqkWD/btsIGxhgfan/SLjq6AMRSOG8fblrpc9Ul0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNqkWD%2FbtsIGxhgfan%2FSLjq6AMRSOG8fblrpc9Ul0%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;747&quot; height=&quot;213&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://codingfarm.tistory.com/538&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;b&gt;accept() 함수가 첫 번째 요청을 꺼내는데 이 소켓이 바로 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;클라이언트 소켓&lt;/span&gt;&lt;/b&gt;이다. 즉, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;실제 클라이언트와 통신하는 소켓&lt;/b&gt;&lt;/span&gt;이라는 것이다. 왜 이렇게 소켓을 분리했을까. 그것은 &lt;u&gt;&lt;b&gt;통신하는 중에도 다른 클라이언트의 요청을 받아들일 수 있도록 하기 위함&lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 송수신하기 위해서 read() 와 write() 함수를 사용한다. 여기서 write()는 데이터를 보내는 주체가 자기 자신이다. 그래서 보낼 데이터의 양을 알 수 있지만 &lt;b&gt;read() 함수&lt;/b&gt;는 그렇지 않다. 즉, &lt;b&gt;한번 실행하면 언제 끝날지 모르는 상태&lt;/b&gt;가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;read() 함수가 종료되는 순간은 클라이언트에서 close() 를 수행하여 서버에 EOF를 보냈을 때&lt;/b&gt;이다. 결론적으로 &lt;u&gt;&lt;b&gt;모든 클라이언트를 위해 시스템을 동기화할 수 없다.&lt;/b&gt;&lt;/u&gt; 그래서 송수신 작업을 하는 함수를 스레드에 올려 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;비동기적으로 작업&lt;/b&gt;&lt;/span&gt;해야 여러 클라이언트의 요청을 처리하고 응답할 수 있다. 유닉스 계열 POSIX 시스템에서는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;pthread.h 헤더 파일&lt;/b&gt;&lt;/span&gt;을 제공하여 이를 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;pthread.h: POSIX Thread의 약자로 유닉스계열 POSIX 시스템에서 병렬적으로 작동하는 소프트웨어를 작성하기 위해 제공하는 API (멀티 스레딩)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  구현&lt;/b&gt;&lt;/h3&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; &amp;zwj;  Server, Client &amp;gt; MySQL&amp;nbsp; 접속&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;MySQL 을 사용하기 위해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&amp;lt;mysql/mysql.h&amp;gt;&lt;/span&gt; 헤더 파일&lt;/b&gt;을 넣어야 한다. 실습 환경인 Ubuntu에서 MySQL을 사용하기 위해서는 MySQL을 설치하고 계정을 생성하는 등의 작업이 필요하다. 아래 링크를 참고하면 도움이 될 것이다.&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;a href=&quot;https://olppaemmit.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://olppaemmit.tistory.com/27&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721544305009&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Linux] Ubuntu에 MySQL 설치 및 사용(외부 접속)&quot; data-og-description=&quot;1. MySQL 설치 2. conf 파일 수정 3. port 열어주기 4. MySQL 시작 5. MySQL 접속 6. MySQL 권한 부여 7. VirtualBox에서 포트포워딩 8. MySQL 외부접속 1. MySQL 설치하기 sudo apt-get install mysql-server 2. conf 파일 수정(port &quot; data-og-host=&quot;olppaemmit.tistory.com&quot; data-og-source-url=&quot;https://olppaemmit.tistory.com/27&quot; data-og-url=&quot;https://olppaemmit.tistory.com/27&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXi994/hyWCNP3HwB/Bktk00Xy2sjh8lEWG6k2fk/img.png?width=800&amp;amp;height=586&amp;amp;face=0_0_800_586,https://scrap.kakaocdn.net/dn/buVXkd/hyWCKZ6boP/FxHXSPbs5wpEpYR6XkT2JK/img.png?width=800&amp;amp;height=586&amp;amp;face=0_0_800_586,https://scrap.kakaocdn.net/dn/dEefII/hyWCLdC5vk/yp7yb5TdDofjeB1OWxq0Ck/img.png?width=1496&amp;amp;height=839&amp;amp;face=0_0_1496_839&quot;&gt;&lt;a href=&quot;https://olppaemmit.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://olppaemmit.tistory.com/27&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXi994/hyWCNP3HwB/Bktk00Xy2sjh8lEWG6k2fk/img.png?width=800&amp;amp;height=586&amp;amp;face=0_0_800_586,https://scrap.kakaocdn.net/dn/buVXkd/hyWCKZ6boP/FxHXSPbs5wpEpYR6XkT2JK/img.png?width=800&amp;amp;height=586&amp;amp;face=0_0_800_586,https://scrap.kakaocdn.net/dn/dEefII/hyWCLdC5vk/yp7yb5TdDofjeB1OWxq0Ck/img.png?width=1496&amp;amp;height=839&amp;amp;face=0_0_1496_839');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Linux] Ubuntu에 MySQL 설치 및 사용(외부 접속)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. MySQL 설치 2. conf 파일 수정 3. port 열어주기 4. MySQL 시작 5. MySQL 접속 6. MySQL 권한 부여 7. VirtualBox에서 포트포워딩 8. MySQL 외부접속 1. MySQL 설치하기 sudo apt-get install mysql-server 2. conf 파일 수정(port&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;olppaemmit.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 C 에서 사용하는 MySQL 명령어는 아래 링크를 참고하면 도움이 될 것이다.&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;a href=&quot;https://onecellboy.tistory.com/161&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://onecellboy.tistory.com/161&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721544334560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[C 언어] mysql c언어 api라이브러리 사용법  - 펌&quot; data-og-description=&quot;출처 : http://koronaii.tistory.com/194 MySQL C API 1) my_ulonglong mysql_affected_rows(MYSQL* mysql) INSERT, UPDATE, DELETE 등의 query로 영향을 받은 ROW의 수를 리턴한다. 2) void mysql_close(MYSQL* mysql) 서버와의 연결을 종료한다&quot; data-og-host=&quot;onecellboy.tistory.com&quot; data-og-source-url=&quot;https://onecellboy.tistory.com/161&quot; data-og-url=&quot;https://onecellboy.tistory.com/161&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cO0G8t/hyWCH3lU75/aPMtYEy9gJfX2uWyQMT7kk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cpnLGc/hyWCDmnjH8/11c86E8cQI9N6nS0vOaYvk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bVI3DY/hyWCGDoX24/CMMqpxKWL0VpUmzS967aA0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://onecellboy.tistory.com/161&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://onecellboy.tistory.com/161&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cO0G8t/hyWCH3lU75/aPMtYEy9gJfX2uWyQMT7kk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cpnLGc/hyWCDmnjH8/11c86E8cQI9N6nS0vOaYvk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bVI3DY/hyWCGDoX24/CMMqpxKWL0VpUmzS967aA0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[C 언어] mysql c언어 api라이브러리 사용법 - 펌&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;출처 : http://koronaii.tistory.com/194 MySQL C API 1) my_ulonglong mysql_affected_rows(MYSQL* mysql) INSERT, UPDATE, DELETE 등의 query로 영향을 받은 ROW의 수를 리턴한다. 2) void mysql_close(MYSQL* mysql) 서버와의 연결을 종료한다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;onecellboy.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721544410580&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
#include &amp;lt;mysql/mysql.h&amp;gt;

MYSQL *con;                   // SQL connection
MYSQL_RES *sql_result = NULL; // SQL 응답
MYSQL_ROW sql_row;            // SQL 결과 배열

int main(int argc, char *argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    pthread_t send_thread, recv_thread; // 송신 스레드, 수신 스레드
    void *thread_return;                // pthread_join에 사용
    int str_len;
    
    // MYSQL
    con = mysql_init(NULL);

    if (con == NULL)
    {
        fprintf(stderr, &quot;%s\n&quot;, mysql_error(con));
        exit(1);
    }

    if (mysql_real_connect(con, &quot;localhost&quot;, &quot;user1&quot;, &quot;0000&quot;, &quot;chatdb&quot;, 3306, NULL, 0) == NULL)
    {
        finish_with_error(con);
    }
    
    ...&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;C에서 MySQL이라는 타입으로 connection을 생성할 수 있다. 이를 초기화하고 생성한 계정을 통해 접속하는 코드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;  Client &amp;gt; 소켓 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721544716428&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{
    ...
    sock = socket(PF_INET, SOCK_STREAM, 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;&lt;b&gt;'sock' 이라는 Socket 식별자에 socket() 함수를 통해 생성한 소켓을 저장&lt;/b&gt;한다. PF_INET은 프로토콜을 지정한다는 것인데 리눅스에서는 AF_INET과 서로 같은 상수를 갖고 있어 구분하지 않아도 된다. SOCK_STREAM은 TCP 통신을 의미하며, 0은 별도 프로토콜을 지정하지 않겠다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &amp;zwj;  Client &amp;gt; 서버 정보 바인딩&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721544920393&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{
    ...

    // 연결할 서버 정보 할당
    memset(&amp;amp;serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_addr.sin_port = htons(atoi(argv[2]));
    
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inet_addr() 함수와 htons() 함수를 통해 네트워크 바이트 순서에 맞게 주소를 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  Server &amp;gt; 듣기 소켓 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721546325986&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

#include &amp;lt;pthread.h&amp;gt;

/*
여러 명의 클라이언트가 접속하므로 클라이언트 소켓은 배열
멀티스레드 시, clnt_cnt와 clnt_socks 에 여러 스레드가 접속할 수 있기 때문에
두 변수를 사용하는 영역은 임계 영역
*/
int clnt_cnt = 0; // 접속한 클라이언트 수
int clnt_socks[MAX_CLNT];
pthread_mutex_t mtx; // mutex 선언 (스레드끼리 전역변수 동시 사용 방지)

int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock; // serv_sock(듣기 소켓), clnt_sock(연결 소켓)
    struct sockaddr_in serv_addr, clnt_addr;
    pthread_t tid; // thread 선언
    socklen_t clnt_addr_size;
    
    ...
    
    pthread_mutex_init(&amp;amp;mtx, NULL); // mutex 생성

    if ((serv_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) // 듣기 소켓 생성
    {
        error_handling(&quot;socket() error&quot;);
    }
    
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;듣기 소켓을 생성하기 앞서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;mutex를&lt;/b&gt;&lt;/span&gt; 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutex는 pthread에서 지원하는 기능으로 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;여러 스레드를 실행하는 환경에서 자원에 대한 접근 제한을 위한 동기화&lt;/b&gt;&lt;b&gt;를 지원&lt;/b&gt;&lt;/span&gt;한다. 클라이언트 요청을 비동기적으로 처리하지만 &lt;u&gt;&lt;b&gt;모든 클라이언트가 사용하는 변수는 동기화를 해야 동시 수정으로 인한 일관성 오류를 막을 수 있다.&lt;/b&gt;&lt;/u&gt; 바로 &lt;b&gt;클라이언트의 수를 카운팅하는&lt;/b&gt; &lt;b&gt;clnt_cnt&lt;/b&gt;와 앞서 그림에서 본 &lt;b&gt;클라이언트 소켓들을 저장&lt;/b&gt;하는 &lt;b&gt;clnt_socks 배열&lt;/b&gt;이 대상이다. 위 코드에서는 사용할 mutex를 초기화하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; 듣기 소켓에 서버 주소 바인딩&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721546850320&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{
    ...

    // 주소 정보 바인딩
    printf(&quot;set server addr...\n&quot;);
    memset(&amp;amp;serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(atoi(argv[1]));
    
    printf(&quot;binding...\n&quot;);
    if (bind(serv_sock, (struct sockaddr *)&amp;amp;serv_addr, sizeof(serv_addr)) == -1) // 소켓과 주소 정보 결합
    {
        error_handling(&quot;bind() error&quot;);
    }
    
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 서버 주소를 바인딩하면 클라이언트가 요청한 서버 주소와 동일한 주소인지 듣기 소켓이 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; 클라이언트 연결 요청 대기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721547097887&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{
    // 5는 큐의 크기
    // 웹 서버같이 수 천명의 클라이언트로 바쁠 경우, 15로 잡는 경우가 보통
    if (listen(serv_sock, 5) == -1)
    {
        error_handling(&quot;listen() error&quot;);
    }

    printf(&quot;waiting...\n&quot;);
    
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐의 크기를 설정하고 클라이언트 요청을 대기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;  Client &amp;gt; 서버에 연결 요청&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721547139206&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{
    ...
    
    // 서버에 연결 요청
    if (connect(sock, (struct sockaddr *)&amp;amp;serv_addr, sizeof(serv_addr)) &amp;lt; 0)
    {
        perror(&quot;connect&quot;);
        return -1;
    }
    
    ...&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;connect() 함수를 통해 소켓 식별자에 서버 정보를 바인딩한 구조체(serv_addr)의 주소를 넘겼다. 만약 음수가 나온다면 연결에 실패한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; 클라이언트 소켓 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721547478020&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

#include &amp;lt;pthread.h&amp;gt;

int main(int argc, char *argv[])
{
    ...
    
    while (1)
    {
        clnt_addr_size = sizeof(clnt_addr);
        // clnt_addr -&amp;gt; 연결된 클라이언트의 주소 정보
        if ((clnt_sock = accept(serv_sock, (struct sockaddr *)&amp;amp;clnt_addr, &amp;amp;clnt_addr_size)) &amp;lt; 0)
        {
            error_handling(&quot;accept() error&quot;);
        }

        printf(&quot;[NOW] accept\n&quot;);
        
        pthread_mutex_lock(&amp;amp;mtx);           // 전역 변수 사용을 위해 mutex 락
        clnt_socks[clnt_cnt++] = clnt_sock; // 소켓 배정
        pthread_mutex_unlock(&amp;amp;mtx);         // mutex 언락
        
    ...&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;듣기 소켓을 통해 적재해 둔 연결 요청으로부터 accept() 함수를 통해 클라이언트 요청을 생성했다. 그 후에 클라이언트 소켓을 저장하는 clnt_socks 배열에 현재 클라이언트 소켓을 저장했다.&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;u&gt;&lt;b&gt;clnt_socks 배열은 클라이언트 전역 변수이므로 pthread_mutex_lock()과 pthread_mutex_unlock() 안에서 작업해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;  Client &amp;gt; 최근 채팅 10개 로그 출력 요청&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721547816107&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MYSQL *con;                   // SQL connection
MYSQL_RES *sql_result = NULL; // SQL 응답
MYSQL_ROW sql_row;            // SQL 결과 배열

int main(int argc, char *argv[])
{
    ...

    if (mysql_query(con, &quot;SELECT * FROM CHAT ORDER BY date DESC LIMIT 10&quot;) != 0) // 최근 10개 레코드 조회
        finish_with_error(con);

    sql_result = mysql_store_result(con); // 쿼리 결과 호출

    while ((sql_row = mysql_fetch_row(sql_result)) != NULL)
    {
        printf(&quot;%s : %s\n&quot;, sql_row[0], sql_row[1]);
    }
    
    ...&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;클라이언트는 쿼리를 통해 최근 10개 레코드를 요청했다. 테이블 상태는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csOvM5/btsIIoJPqnS/Lj31gmsyFl2yrwp23NHsk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csOvM5/btsIIoJPqnS/Lj31gmsyFl2yrwp23NHsk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csOvM5/btsIIoJPqnS/Lj31gmsyFl2yrwp23NHsk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsOvM5%2FbtsIIoJPqnS%2FLj31gmsyFl2yrwp23NHsk0%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;645&quot; height=&quot;233&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;243&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;b&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;MYSQL_RES&lt;/span&gt; 타입 변수를 통해 저장된 쿼리 결과를 받을 수 있다. 그 후에 결과를 모두 fetch 하여 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;MYSQL_ROW&lt;/span&gt; 변수로 받으면 튜플 단위로 필드마다 배열로 저장된다.&lt;/b&gt; 이를 출력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAFqI5/btsIGVPEhTm/wQPRtmHZkkf90DXt53gEkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAFqI5/btsIGVPEhTm/wQPRtmHZkkf90DXt53gEkK/img.png&quot; data-alt=&quot;로그 데이터 형식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAFqI5/btsIGVPEhTm/wQPRtmHZkkf90DXt53gEkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAFqI5%2FbtsIGVPEhTm%2FwQPRtmHZkkf90DXt53gEkK%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;347&quot; height=&quot;562&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그 데이터 형식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;  데이터 전송&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721548301736&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

#include &amp;lt;pthread.h&amp;gt;

#define BUF_SIZE 100
#define NAME_SIZE 30
#define TIME_SIZE 100

void *send_msg(void *arg);
void *recv_msg(void *arg);
void finish_with_error(MYSQL *con);

int main(int argc, char *argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    pthread_t send_thread, recv_thread; // 송신 스레드, 수신 스레드
    void *thread_return;                // pthread_join에 사용
    int str_len;

    ...

    // 송신과 수신을 수행할 두 스레드 생성
    // 연결 요청 대상인 서버는 동일하므로 매개변수는 sock으로 동일
    pthread_create(&amp;amp;send_thread, NULL, send_msg, (void *)&amp;amp;sock);
    pthread_create(&amp;amp;recv_thread, NULL, recv_msg, (void *)&amp;amp;sock);

    // 스레드 종료 대기
    pthread_join(send_thread, NULL);
    pthread_join(recv_thread, NULL);

    close(sock); // 클라이언트 연결 종료

    mysql_free_result(sql_result); // SQL 응답 포인터 해제

    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;&lt;u&gt;&lt;b&gt;데이터를 송수신하는 작업은 클라이언트끼리 동기화할 수 없다.&lt;/b&gt;&lt;/u&gt; 따라서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;pthread_create()를&lt;/b&gt;&lt;/span&gt; 통해 &lt;b&gt;데이터를 전송하고 수신하는 함수를 스레드에 올려서 동작하도록 했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스레드는 종료되어도 즉시 메모리 자원이 해제되지 않는다.&lt;/b&gt; 이를 위해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;pthread_join()&lt;/b&gt;&lt;/span&gt; 함수를 사용한다. &lt;u&gt;&lt;b&gt;이 함수를 만나면 스레드는 자원을 해제하기 때문에 메모리 누수가 발생하지 않으려면 스레드마다 해당 함수를 사용해야 한다.&lt;/b&gt;&lt;/u&gt; pthread_join()의 영향을 받는 스레드는 기본 스레드인 joinable 스레드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721548566083&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
#include &amp;lt;time.h&amp;gt;

char msg[BUF_SIZE];
char name[NAME_SIZE] = &quot;[DEFAULT]&quot;; // 채팅창에 보여질 이름의 형태(20자 제한)
char logout[] = &quot;님이 로그아웃했습니다.\n&quot;;

...

void *send_msg(void *arg)
{
    int sock = *((int *)arg);                        // void descriptor -&amp;gt; int 변환
    char name_msg[TIME_SIZE + NAME_SIZE + BUF_SIZE]; // 사용자 ID와 메시지를 합칠 것임
    char logout_msg[NAME_SIZE + strlen(logout)];     // 사용자 ID와 로그아웃 메시지를 합칠 것임
    char local_date_time[TIME_SIZE];                 // 포맷팅한 시간 정보
    time_t now = time(NULL);                         // 현재 시간
    struct tm *t = localtime(&amp;amp;now);                  // 시간 포맷팅

    while (1)
    {
        fgets(msg, BUF_SIZE, stdin); // 사용자 입력을 msg에 저장

        asctime_r(t, local_date_time); // 현재 시간 갱신

        if (!strcmp(msg, &quot;exit\n&quot;))
        {
            sprintf(logout_msg, &quot;%s %s&quot;, name, logout);  // (사용자 ID + 로그아웃 메시지) 를 버퍼에 저장
            write(sock, logout_msg, sizeof(logout_msg)); // 서버로 전송하여 모든 클라이언트에 뿌림

            close(sock); // 서버에 EOF 보냄
            exit(1);
        }

        // 생성된 name_msg를 출력
        sprintf(name_msg, &quot;%s &amp;gt; %s %s&quot;, local_date_time, name, msg); // ID가 joo이고 메시지가 &quot;Hi&quot; 라면, [joo] Hi

        write(sock, name_msg, sizeof(name_msg)); // 서버로 채팅을 보냄
    }

    return NULL;
}&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;사용자 입력을 fgets()를 통해 받는다. fgets()는 공백이나 '\n'에 상관없이 마지막에 NULL('\0')만 붙이기 때문에 가장 안전하다고 판단하여 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 시간을 채팅에 포함하기 위해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;time.h&lt;/span&gt; 헤더 파일&lt;/b&gt;을 사용했다. 해당 헤더 파일에서 제공하는 time() 함수를 사용하면 현재 시간을 얻을 수 있다. 앞서 sockaddr 구조체처럼 시간도 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;tm이라는 구조체&lt;/b&gt;&lt;/span&gt;&lt;b&gt;로 받을 수 있다.&lt;/b&gt; 그러면 아래와 같은&amp;nbsp; tm 필드에 값이 적절히 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6LHaf/btsIGFfe9EV/bPcESw0CUoMm3wOKqL2AF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6LHaf/btsIGFfe9EV/bPcESw0CUoMm3wOKqL2AF1/img.png&quot; data-alt=&quot;tm 구조체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6LHaf/btsIGFfe9EV/bPcESw0CUoMm3wOKqL2AF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6LHaf%2FbtsIGFfe9EV%2FbPcESw0CUoMm3wOKqL2AF1%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;298&quot; height=&quot;423&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2839&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tm 구조체&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;asctime()이라는 함수는 이 내용을 토대로 시간 정보를 좀 더 알아보기 좋게 포맷팅 해준다. asctime_r() 함수는 이 기능에 자동으로 시간을 갱신해 주는 기능이 추가되었다.&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: #ffffdd;&quot;&gt;&lt;b&gt;동작 방식은 채팅 출력문을 포맷팅 한 후 서버 소켓으로 write() 하는 것&lt;/b&gt;&lt;/span&gt;이다. 그리고 'exit'이라는 문자열을 적으면 로그아웃 즉 EOF를 전송하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; 클라이언트가 전송한 메시지를 모든 클라이언트에 전송하고 저장&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721549658469&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{

    while (1)
    {
        ...

        pthread_create(&amp;amp;tid, NULL, handle_clnt, (void *)&amp;amp;clnt_sock); // handle_clnt를 작업하는 스레드 생성
        pthread_detach(tid);                                         // 해당 스레드 분리
        printf(&quot;accepted host(IP: %s, Port: %d)\n&quot;, inet_ntoa(clnt_addr.sin_addr), ntohs(serv_addr.sin_port));

        mysql_free_result(sql_result); // SQL 응답 포인터 해제
    }
    
    close(serv_sock);
    mysql_close(con);

    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;서버도 클라이언트 요청을 처리하는 함수를 pthread를 통해 생성한 스레드에 올렸다. &lt;b&gt;read() 작업이 종료된 후 close()를 요청한 클라이언트의 클라이언트 소켓을 close() 한 후 해당 스레드를 분리&lt;/b&gt;한다. 마찬가지로 &lt;b&gt;스레드를 통해 동작하므로 detach 아래 출력문이 클라이언트가 접속하자마자 출력된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721549878230&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char login[] = &quot;로그인을 완료하였습니다. 로그아웃 명령은 'exit' 입니다.\n&quot;;

...

void *handle_clnt(void *arg)
{
    int clnt_sock = *((int *)arg); // void descriptor -&amp;gt; int 변환
    int str_len = 0;
    char msg[BUF_SIZE];
    char query[QUERY_SIZE + BUF_SIZE];
    char pk[QUERY_SIZE];

    send_msg_me(clnt_sock, login, strlen(login));

    /*
    클라이언트에서 보낸 메시지 받음
    클라이언트에서 EOF를 보내 str_len 이 0이 될때까지 반복
    EOF는 클라이언트에서 소켓을 close 했을 때 보냄
    즉, 클라이언트가 접속을 하고 있는 동안에는 while 문을 벗어나지 않는다.
    */
    while ((str_len = read(clnt_sock, msg, sizeof(msg))) != 0)
    {
        send_msg(msg, str_len); // 접속한 모두에게 메시지 보내기

        // escape single quote
        for (int i = 0; i &amp;lt; str_len; i++)
        {
            if (msg[i] == 39)
            {
                for (int j = str_len; j &amp;gt; i; j--)
                {
                    msg[j] = msg[j - 1];
                }
                i += 2;
            }
        }

        now = time(NULL); // 현재 시간

        sprintf(pk, &quot;%ld&quot;, now);
        sprintf(query, &quot;INSERT INTO CHAT VALUES('%s', '%s')&quot;, pk, msg); // 채팅 쿼리 생성
        if (mysql_query(con, query))                                    // 채팅 저장
            finish_with_error(con);
    }

    // while 문을 탈출 -&amp;gt; 현재 담당하는 소켓의 연결이 끊김

    ...

}

// 접속한 모두에게 메시지 보내기
void send_msg(char *msg, int len)
{
    pthread_mutex_lock(&amp;amp;mtx); // 전역 변수 사용을 위해 mutex 락
    for (int i = 0; i &amp;lt; clnt_cnt; i++)
        write(clnt_socks[i], msg, len); // 모든 클라이언트 소켓에 메시지 전달

    pthread_mutex_unlock(&amp;amp;mtx); // mutex 언락
}

// 접속한 대상에만 메시지 보내기
void send_msg_me(int clnt_sock, char *msg, int len)
{
    write(clnt_sock, msg, len); // 접속 클라이언트 소켓에 메시지 전달
}

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 전송한 데이터와 클라이언트 소켓을 처리하는 함수다.&lt;/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;send_msg_me() 함수를 통해 접속한 클라이언트에게 로그인 메시지를 전송한다.&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;서버는 read() 함수를 통해 클라이언트 측에서 write() 한 데이터가 있는지 주시한다. 그러다가 데이터가 들어오면 모든 클라이언트에게 입력한 내용을 보여준다. 이는 설계상 단체 채팅 프로그램이기 때문이다. 모든 클라이언트에게 메시지를 전송하려면 클라이언트 소켓 목록을 가져와서 반복문을 통해 메시지를 보여줘야 한다. 이 작업은 &lt;u&gt;&lt;b&gt;클라이언트 전역 변수를 사용하기 때문에 mutex 락을 걸고 수행해야 한다.&lt;/b&gt;&lt;/u&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;앞서 테이블의 필드를 보면 Primary Key로 'date'가 지정되어 있었다. 이는 현재 시간을 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;10자리 정도의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 숫자로 저장한 것이며, 기본키 역할을 한다. 이렇게 완성된 하나의 튜플을 INSERT문을 통해 MySQL에 로그로서 저장한다.&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;while 문을 탈출하게 되면 EOF를 받은 것이며, 연결 종료 작업을 수행해야 한다. 그래서 다시 mutex를 열어 해당 클라이언트 소켓을 제거하는 작업을 수행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &amp;zwj;  데이터 수신&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721549315037&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void *recv_msg(void *arg)
{
    int sock = *((int *)arg);                        // void descriptor -&amp;gt; int 변환
    char name_msg[TIME_SIZE + NAME_SIZE + BUF_SIZE]; // 사용자 ID와 메시지를 합칠 것임
    int str_len = 0;

    while (1)
    {
        str_len = read(sock, name_msg, sizeof(name_msg) - 1); // 서버에서 들어온 메시지 수신

        name_msg[str_len] = 0; // 버퍼 맨 마지막 값 NULL

        fputs(name_msg, stdout); // 받은 메시지 출력 (서버에서 write 한 메시지)
    }

    return NULL;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 write() 한 데이터가 있다면 이를 읽어 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; 종료를 요청한 클라이언트 소켓 제거&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721550811385&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // while 문을 탈출 -&amp;gt; 현재 담당하는 소켓의 연결이 끊김

    pthread_mutex_lock(&amp;amp;mtx); // 전역 변수 사용을 위해 mutex 락
    // 현재 스레드에서 담당하는 소켓(disconnected) 삭제
    for (int i = 0; i &amp;lt; clnt_cnt; i++)
    {
        if (clnt_sock == clnt_socks[i]) // 현재 담당하는 클라이언트 소켓의 descriptor를 찾으면
        {
            while (i++ &amp;lt; clnt_cnt - 1) // 해당 위치부터 클라이언트 소켓 1칸씩 당기기
                clnt_socks[i] = clnt_socks[i + 1];

            break;
        }
    }

    clnt_cnt--;                 // 클라이언트 수 감소
    pthread_mutex_unlock(&amp;amp;mtx); // mutex 언락

    close(clnt_sock); // 서버의 스레드에서 담당하는 클라이언트 소켓 종료
    
    return NULL;
} //handle_clnt 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Server &amp;gt; MySQL 해제 후 서버 소켓 종료&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721550973935&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char *argv[])
{

    while (1)
    {
        ...

        pthread_detach(tid);                                         // 해당 스레드 분리
        printf(&quot;accepted host(IP: %s, Port: %d)\n&quot;, inet_ntoa(clnt_addr.sin_addr), ntohs(serv_addr.sin_port));

        mysql_free_result(sql_result); // SQL 응답 포인터 해제
    }
    
    close(serv_sock);
    mysql_close(con);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  결과물&lt;/b&gt;&lt;/h3&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1727325349973&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;project-c-2024/chat at main &amp;middot; wndudrla1011/project-c-2024&quot; data-og-description=&quot;Contribute to wndudrla1011/project-c-2024 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&quot; data-og-url=&quot;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yh6cZ/hyW6B3vglN/sCVlzVfNh0ktf7fKoRUTbK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cx3TA0/hyW6IIhybm/uCvuaUYKRqEuUCp81CmsMk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/wndudrla1011/project-c-2024/tree/main/chat&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yh6cZ/hyW6B3vglN/sCVlzVfNh0ktf7fKoRUTbK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cx3TA0/hyW6IIhybm/uCvuaUYKRqEuUCp81CmsMk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;project-c-2024/chat at main &amp;middot; wndudrla1011/project-c-2024&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to wndudrla1011/project-c-2024 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1073&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wLHxs/btsIHlAkVcY/1z3938QeKBbKsAtxHpukr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wLHxs/btsIHlAkVcY/1z3938QeKBbKsAtxHpukr1/img.png&quot; data-alt=&quot;채팅 프로그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wLHxs/btsIHlAkVcY/1z3938QeKBbKsAtxHpukr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwLHxs%2FbtsIHlAkVcY%2F1z3938QeKBbKsAtxHpukr1%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;2000&quot; height=&quot;1073&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1073&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;채팅 프로그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1073&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ao4qe/btsIH2UzbvY/gK4xLs5IE6BgvRJZhskoH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ao4qe/btsIH2UzbvY/gK4xLs5IE6BgvRJZhskoH0/img.png&quot; data-alt=&quot;접속 후 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ao4qe/btsIH2UzbvY/gK4xLs5IE6BgvRJZhskoH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAo4qe%2FbtsIH2UzbvY%2FgK4xLs5IE6BgvRJZhskoH0%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;2000&quot; height=&quot;1073&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1073&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;접속 후 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;References:&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;a href=&quot;https://softtone-someday.tistory.com/20&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://softtone-someday.tistory.com/20&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1720771979916&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[개념정리] 빅엔디안(Big Endian)과 리틀엔디안(Little Endian)&quot; data-og-description=&quot;통신을 하다 보면 통신 패킷이 반대로 나갈 때가 있습니다. 예를 들면 1 2 3 4를 보냈는데 막상 받는 쪽에서 들어온 패킷은 4 3 2 1인 거죠 이는 컴퓨터 CPU의 데이터를 저장하는 순서에서 발생하는 &quot; data-og-host=&quot;softtone-someday.tistory.com&quot; data-og-source-url=&quot;https://softtone-someday.tistory.com/20&quot; data-og-url=&quot;https://softtone-someday.tistory.com/20&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/banegO/hyWzDmdrC9/ZmvneNKZgBqMXOOPhEaYO0/img.png?width=498&amp;amp;height=303&amp;amp;face=0_0_498_303,https://scrap.kakaocdn.net/dn/bQzjjn/hyWzB9MvT4/2gjgThPbTzR2I235svWGy1/img.png?width=498&amp;amp;height=303&amp;amp;face=0_0_498_303,https://scrap.kakaocdn.net/dn/eDDw4/hyWzCU9tNJ/o0i4eweGUcpQA3bduKw8nK/img.jpg?width=1368&amp;amp;height=1080&amp;amp;face=0_0_1368_1080&quot;&gt;&lt;a href=&quot;https://softtone-someday.tistory.com/20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://softtone-someday.tistory.com/20&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/banegO/hyWzDmdrC9/ZmvneNKZgBqMXOOPhEaYO0/img.png?width=498&amp;amp;height=303&amp;amp;face=0_0_498_303,https://scrap.kakaocdn.net/dn/bQzjjn/hyWzB9MvT4/2gjgThPbTzR2I235svWGy1/img.png?width=498&amp;amp;height=303&amp;amp;face=0_0_498_303,https://scrap.kakaocdn.net/dn/eDDw4/hyWzCU9tNJ/o0i4eweGUcpQA3bduKw8nK/img.jpg?width=1368&amp;amp;height=1080&amp;amp;face=0_0_1368_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[개념정리] 빅엔디안(Big Endian)과 리틀엔디안(Little Endian)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;통신을 하다 보면 통신 패킷이 반대로 나갈 때가 있습니다. 예를 들면 1 2 3 4를 보냈는데 막상 받는 쪽에서 들어온 패킷은 4 3 2 1인 거죠 이는 컴퓨터 CPU의 데이터를 저장하는 순서에서 발생하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;softtone-someday.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev-rootable.tistory.com/163&quot;&gt;https://dev-rootable.tistory.com/163&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721545001631&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;소켓 프로그래밍&quot; data-og-description=&quot;  소켓이란&amp;nbsp;네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부&amp;nbsp;네트워크에 연결하기 위한 소켓은 정해진 규약, 즉 통신을 위한 프로토콜에 맞게 &quot; data-og-host=&quot;dev-rootable.tistory.com&quot; data-og-source-url=&quot;https://dev-rootable.tistory.com/163&quot; data-og-url=&quot;https://dev-rootable.tistory.com/163&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cudL4F/hyWCHWA91B/g0Rpj4tH7ywGmCwnpTXho1/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575,https://scrap.kakaocdn.net/dn/cZhdms/hyWCE6FXZA/vf9WMshbtak1rdXmKZ0pFk/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575,https://scrap.kakaocdn.net/dn/KKmGf/hyWCIHYPQH/IImcfcfv7RBIt16A0ZfhtK/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575&quot;&gt;&lt;a href=&quot;https://dev-rootable.tistory.com/163&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev-rootable.tistory.com/163&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cudL4F/hyWCHWA91B/g0Rpj4tH7ywGmCwnpTXho1/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575,https://scrap.kakaocdn.net/dn/cZhdms/hyWCE6FXZA/vf9WMshbtak1rdXmKZ0pFk/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575,https://scrap.kakaocdn.net/dn/KKmGf/hyWCIHYPQH/IImcfcfv7RBIt16A0ZfhtK/img.png?width=515&amp;amp;height=575&amp;amp;face=0_0_515_575');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;소켓 프로그래밍&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  소켓이란&amp;nbsp;네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부&amp;nbsp;네트워크에 연결하기 위한 소켓은 정해진 규약, 즉 통신을 위한 프로토콜에 맞게&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev-rootable.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Network</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/164</guid>
      <comments>https://dev-rootable.tistory.com/164#entry164comment</comments>
      <pubDate>Fri, 12 Jul 2024 17:29:42 +0900</pubDate>
    </item>
    <item>
      <title>소켓 프로그래밍</title>
      <link>https://dev-rootable.tistory.com/163</link>
      <description>&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  소켓이란&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에 연결하기 위한 소켓은 정해진 규약, 즉 통신을 위한 프로토콜에 맞게 만들어져야 한다. 보통 OSI 7 계층 중 4 계층인 TCP 상에서 동작하는 소켓을 주로 사용하며, 이를 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;TCP 소켓&lt;/b&gt;&lt;/span&gt; 또는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;TCP/IP 소켓&lt;/b&gt;&lt;/span&gt;이라고 부른다. 마찬가지로 UDP에서 동작하는 소켓을 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;UDP 소켓&lt;/b&gt;&lt;/span&gt;이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;소켓은 크게 프로토콜, IP 주소, 포트로 정의된다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  용어 정리&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;프로토콜(Protocol)&lt;/b&gt;&lt;br /&gt;컴퓨터 사이에서 메시지를 주고 받는 데 필요한 양식, 약속이나 규약&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IP 주소(IP Address)&lt;/b&gt;&lt;br /&gt;각 장치(호스트)를 식별하기 위한 고유 주소. 송신자와 수신자를 식별&lt;br /&gt;&lt;br /&gt;&lt;b&gt;포트(Port)&lt;/b&gt;&lt;br /&gt;IP 주소를 통해 도착한 호스트에서 어떤 프로세스와 통신할지에 대한 정보. 즉, 프로세스를 구분하는 번호&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  소켓 프로그래밍&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rH8zs/btsIp8O5YJO/MkHS7eoyvdOaMdTEOaeJZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rH8zs/btsIp8O5YJO/MkHS7eoyvdOaMdTEOaeJZ1/img.png&quot; data-alt=&quot;소켓 프로그래밍 개요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rH8zs/btsIp8O5YJO/MkHS7eoyvdOaMdTEOaeJZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrH8zs%2FbtsIp8O5YJO%2FMkHS7eoyvdOaMdTEOaeJZ1%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;481&quot; height=&quot;537&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소켓 프로그래밍 개요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 주고 받기 위해서는 소켓의 연결 과정이 선행되어야 하고, 그 과정에서 연결 요청과 수락이 각각 클라이언트 소켓과 서버 소켓의 역할이다.&lt;/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: #ffffdd;&quot;&gt;&lt;b&gt;클라이언트 소켓&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;IP 주소와 포트 번호를 통해 서버 소켓에게 연결을 시도&lt;/b&gt;하고, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;서버 소켓&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;어떤 연결 요청(포트 번호로 식별)을 받아들일지 미리 시스템에 등록&lt;/b&gt;하여 요청이 수신되었을 때 해당 &lt;b&gt;요청을 처리&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 연결이 완료된 후 클라이언트 소켓과 서버 소켓은 데이터를 주고받는데, 직접 데이터를 주고받는 것이 아니라 &lt;b&gt;서버 소켓은 클라이언트 소켓의 연결 요청을 받아들이는 역할만 수행&lt;/b&gt;할 뿐이다. 직접적인 데이터 송수신은 서버 소켓의 연결 요청 수락 결과로 만들어지는 새로운 소켓을 통해 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  Creating a &lt;/b&gt;&lt;b&gt;socket&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;데이터를 송수신하기 위한 통로(소켓) 생성&lt;/blockquote&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;u&gt;&lt;b&gt;소켓 통신을 하기 위해서는 먼저 소켓을 생성해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720400544786&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#incldue &amp;lt;sys/socket.h&amp;gt;

int socket(int domain, int type, int protocol);&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;위와 같은 socket 함수를 통해 소켓을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  socket 함수 파라미터&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;domain&lt;/b&gt;&lt;br /&gt;통신에 사용되는 protocol family를 지정한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;protocol family&lt;/b&gt;&lt;br /&gt;소켓을 생성할 때 이 소켓이 어떤 프로토콜을 사용해 통신을 할지 정하는 정보&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzci5P/btsIqaM146g/yUX7crFGnczkcdjT9VZDFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzci5P/btsIqaM146g/yUX7crFGnczkcdjT9VZDFk/img.png&quot; data-alt=&quot;출처: https://seongmok.com/43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzci5P/btsIqaM146g/yUX7crFGnczkcdjT9VZDFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdzci5P%2FbtsIqaM146g%2FyUX7crFGnczkcdjT9VZDFk%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;413&quot; height=&quot;243&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://seongmok.com/43&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;&lt;b&gt;type&lt;/b&gt;&lt;br /&gt;SOCK_STREAM은 TCP 통신 체계에서 사용되고, SOCK_DGRAM은 UDP 통신 체계에서 사용된다.&lt;br /&gt;두 가지 타입을 가장 많이 사용하며, 각 방식은 해당 통신 체계의 성질과 호환된다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;protocol&lt;/b&gt;&lt;br /&gt;해당 소켓에서 사용되는 별도의 protocol이 필요할 경우 protocol 파라미터로 지정해 주면 된다. 필요하지 않을 경우 보통 0을 전달한다.&lt;/blockquote&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;socket()은 system call로 반환 값은 socket descriptor이다. 이때 소켓 생성에 실패하면 -1을 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720402555049&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;

int sockfd;

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) &amp;lt; 0) {
    //print &quot;socket error + the error message&quot;
    perror(&quot;socket error&quot;);
    exit(1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 소켓이 생성되는 시점에는 연결 대상에 대한 어떠한 정보도 담겨 있지 않다. 따라서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;연결 대상(IP:Port)을 지정하고 연결 요청을 전달하기 위해서는 connect() API를 호출해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  Binding the local address ( bind() )&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;소켓에 주소를 할당하기 위한 시스템 콜&lt;/blockquote&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와 같은 프로토콜을 사용한다면 각 소켓은 시스템이 관리하는 포트(0 ~ 65535) 중 하나의 포트 번호를 사용하게 된다. 여기서 &lt;b&gt;서로 다른 소켓이 같은 포트 번호를 사용하는 일이 생길 수 있어 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;운영체제는 내부적으로 포트 번호와 소켓 연결 정보를 관리&lt;/span&gt;&lt;/b&gt;한다. 그리고 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;bind() API는 해당 소켓이 지정된 포트 번호를 사용할 것이라는 것을 운영체제에 요청하는 역할&lt;/b&gt;&lt;/span&gt;을 한다. 만약 지정된 포트 번호를 다른 소켓이 사용하고 있다면 bind() API는 에러를 리턴한다.&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: #ffffdd;&quot;&gt;&lt;b&gt;소켓과 포트 번호를 결합&lt;/b&gt;&lt;/span&gt;해야 하는데 이 때 사용하는 API가 bind()인 것이다.&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;✅ bind API&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720406366050&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int bind(int sockfd, struct sockaddr *addr, int addr_len)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  파라미터 설명&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;int sockfd&lt;/b&gt;: socket() 결과로 반환된 socket descriptor&lt;br /&gt;&lt;b&gt;struct sockaddr *addr&lt;/b&gt;: socket의 구조체 포인터 (자신의 address + port number)&lt;br /&gt;&lt;b&gt;int addr_len&lt;/b&gt;: 구조체의 길이&lt;/blockquote&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;u&gt;성공 시 0을 리턴, 실패 시 -1을 리턴&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ 소켓 구조체 (sockaddr)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720406681148&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define &amp;lt;netinet/in.h&amp;gt;

struct sockaddr {
    u_char sa_len; //length : used in kernel
    u_short sa_family; //address family
    char sa_data[14]; //address
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✅ sockaddr_in&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sa_data [14]를 조금 더 세분화해서 나눈 sockaddr_in&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720406988955&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//&amp;lt;netinet/in.h&amp;gt; 헤더 파일에 정의되어 있음

struct sockaddr_in {
    u_char sin_len; //length
    u_short sin_family; //AF_INET
    u_short sin_port; //port number (2 byte)
    struct in_addr sin_addr; //IP address (4 byte)
    char sin_zero[8]; //unused (8byte), fill the zero
}

struct in_addr {
    u_long s_addr; //32 bit IP address
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rON8h/btsIscCCQPB/lGYvj72ZvtMKjPAskzw8mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rON8h/btsIscCCQPB/lGYvj72ZvtMKjPAskzw8mK/img.png&quot; data-alt=&quot;출처: https://seongmok.com/43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rON8h/btsIscCCQPB/lGYvj72ZvtMKjPAskzw8mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrON8h%2FbtsIscCCQPB%2FlGYvj72ZvtMKjPAskzw8mK%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;484&quot; height=&quot;371&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://seongmok.com/43&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720411365387&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#define MYPORT 50000

int sockfd;
struct sockaddr_in my_addr; //socket 구조체 선언

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) &amp;lt; 0) { //소켓 생성
    //print &quot;socket error + the error message&quot;
    perror(&quot;socket error&quot;);
    exit(1);
}

memset(&amp;amp;my_addr, 0, sizeof(my_addr)); //socket 구조체 초기화

my_addr.sin_family = AF_INET; //address family(PF_INET과 동일)

/*
자신의 포트 번호를 htons를 통해 변환
htons는 host에서 사용하는 number를 network에서 사용가능하도록 short하게 변환함
*/
my_addr.sin_port = htons(MYPORT);

/*
INADDR_ANY는 현재 나의 주소 반환
htonl은 host에서 사용하는 주소를 network에서 사용가능하도록 long하게 변환함
*/
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sockfd, (struct sockaddr *)&amp;amp;my_addr, sizeof(my_addr)) &amp;lt; 0) {
    perror(&quot;bind error&quot;);
    exit(1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  htonl(), htons(), ntohl(), ntohs()&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;htons() - Host to Network Short&lt;br /&gt;htonl() - Host to Network Long&lt;br /&gt;ntohs() - Network to Host Short&lt;br /&gt;ntohl() - Network to Host Long&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;소켓을 통해 다른 기종 간에 데이터를 송수신할 때, Host System의 Byte Order에 맞게 데이터를 변환해 주기 위해 사용된다.&lt;/b&gt;&lt;/span&gt; &lt;u&gt;&lt;b&gt;반드시 네트워크에 보내기 전에 네트워크 바이트 순서로 바꿔서 보내야 한다.&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;sin_port, sin_addr은 네트워크 바이트 순서로 기록하지만 sin_family는 그렇지 않다. sin_port와 sin_addr은 캡슐화되어 네트워크로 전송되어야 하는 변수인 것이다. 따라서 Network-Byte-Order(Big Endian)이어야 한다. 반면, sin_family는 시스템 내부에서 커널에 의해서만 사용되는 변수이며 네트워크로 전송되지 않으므로 Host-Byte Order(Little Endian)로 기록되어야 하는 것이다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;htonl 함수&lt;/b&gt;&lt;br /&gt;&lt;b&gt;32 bit Little Endian ➡ Big Endian (in TCP/IP)로 변환하는 함수&lt;/b&gt;이다.&lt;br /&gt;sockaddr_in 구조체에 저장되기 전에 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;32 bit로 처리되는 IP Address를 Big Endian으로 변환&lt;/b&gt;&lt;/span&gt;하는데 주로 사용된다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;htons 함수&lt;/b&gt;&lt;br /&gt;&lt;b&gt;32 bit Little Endian ➡ Big Endian (in TCP/IP) 로 변환하는 함수&lt;/b&gt;이다.&lt;br /&gt;sockaddr_in 구조체에 저장되기 전에 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;32 bit로 처리되는 Port Number를 Big Endian으로 변환&lt;/b&gt;&lt;/span&gt;하는데 주로 사용된다.&lt;/blockquote&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;  Name-to-Address Conversion&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INADDR_ANY와 같이 주소를 바로 넘겨줄 수 있지만, 이름만 알고 있을 때 그 이름을 주소로 변경하는 과정이 필요하다. 또한 반대로 주소를 호스트 이름으로 변경할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gethostbyname(): hostname ➡ address&lt;/li&gt;
&lt;li&gt;gethostbyaddr(): address ➡ hostname&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  IP Address Manipulation&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수를 통해 hostname을 binary한 주소로 바꿨다면 우리가 알아볼 수 있게 변환할 수 있어야 할 것이다. 이를 Dotted decimal이라고 한다. (ex. 11111111.00000000.00000000.00000001 ➡ 255.0.0.1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720414222061&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char *inet_ntoa(struct in_addr address); //IP address -&amp;gt; dotted decimal

/*
dotted decimal -&amp;gt; IP address (return 0 if error)
req: *cp &amp;lt;- dotted decimal
res: *inp &amp;lt;- IP address
*/
char inet_aton(const char *cp, struct in_addr *inp);

u_long inet_addr(char *dottedAddress); //dotted decimal -&amp;gt; directly return IP address (return -1 if error)&lt;/code&gt;&lt;/pre&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;  connect&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;서버 소켓으로 연결 요청&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;connect() API는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;IP 주소와 포트 번호로 식별되는 대상으로 연결 요청&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;을 보낸다. connect() API는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;Block 방식&lt;/b&gt;&lt;/span&gt;으로 동작하기 때문에 연결 요청에 대한 결과가 결정되기 전에는 실행이 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720401980163&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;sys/socket.h&amp;gt;

int connect(int sockfd, struct sockaddr *addr, int addr_len);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;성공 시 0, 실패 시 -1을 리턴한다.&lt;/b&gt;&lt;/u&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;3-way-handshaking 과정을 통한 커넥션을 요청하기 때문에 TCP에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;아주 가끔 UDP에도 사용된다고 한다.&lt;br /&gt;sendto() 함수를 사용할 때 목적지 정보를 매번 넣기 번거로워 최초 connect()를 하여 얻은 정보를 계속 사용한다. 이때 실제 데이터 전송은 없다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  소켓 기술자(socket descriptor)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;UNIX에서 파일을 열면(open)하면 int 타입의 정수를 리턴하는데 이를 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;파일 기술자(file descriptor)&lt;/b&gt;&lt;/span&gt;라고 하며, 파일 기술자를 통해 open된 파일에 접근할 수 있다. 즉, 해당 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;파일에 대한 포인터&lt;/b&gt;&lt;/span&gt;를 가지는 것이다.&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dttHkp/btsIrdhU0gN/9pRLxyKe0hNBMOpxf7uVg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dttHkp/btsIrdhU0gN/9pRLxyKe0hNBMOpxf7uVg1/img.png&quot; data-alt=&quot;출처: http://jkkang.net/unix/netprg/chap2/net2_1.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dttHkp/btsIrdhU0gN/9pRLxyKe0hNBMOpxf7uVg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdttHkp%2FbtsIrdhU0gN%2F9pRLxyKe0hNBMOpxf7uVg1%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;646&quot; height=&quot;327&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: http://jkkang.net/unix/netprg/chap2/net2_1.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;파일 기술자는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;기술자 테이블의 index 번호&lt;/b&gt;&lt;/span&gt;다. 기술자 테이블이란 현재 open되어 있는 파일의 정보들로 구성된 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;구조체를 가리키는 포인터들로 구성된 테이블&lt;/b&gt;&lt;/span&gt;이다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;프로그램에서 소켓을 개설하면 파일 기술자와 똑같은 기능을 하는 소켓 기술자가 리턴된다.&lt;/b&gt;&lt;/u&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 호출이 성공하면 send() / recv() API를 통해 데이터를 주고받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720403602777&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#define DEST_IP &quot;10.12.110.57&quot;
#define DEST_PORT 23

int sockfd;
struct sockaddr_in dest_addr;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) &amp;lt; 0) {
    /* error */
}

memset(&amp;amp;dest_addr, 0, sizeof(dest_addr)); //init memory to 0
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);

if(connect(sockfd, (struct sockaddr *)&amp;amp;dest_addr, sizeof(dest_addr)) &amp;lt; 0) {
    close(sockfd);
    return -1;
}&lt;/code&gt;&lt;/pre&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;  Listen&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;클라이언트 연결 요청 대기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 소켓에 포트 번호를 결합(bind)하고 나면, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;서버 소켓을 통해 클라이언트의 연결 요청 수신을 기다리게 되는데 이 역할을 listen() API가 수행&lt;/b&gt;&lt;/span&gt;한다. 연결 요청이 수신되면 대기 상태를 종료하고 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720415467429&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int listen(int sd, int backlog);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;sd&lt;/b&gt;: socket descriptor&lt;/li&gt;
&lt;li&gt;&lt;b&gt;backlog&lt;/b&gt;: connection 할 때 Q에 몇 개까지의 요청을 pending 상태로 놓을 것인지에 대한 사이즈 (usually 5)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;성공 시 0을, 실패 시 -1 리턴&lt;/b&gt;&lt;/u&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;u&gt;&lt;b&gt;연결 요청을 기다리는 함수이기 때문에 TCP에만 사용된다.&lt;/b&gt;&lt;/u&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;listen() API 가 성공하더라도 요청 성공 또는 실패에 대한 정보만 있고 요청에 대한 정보는 들어 있지 않다. 대신 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;클라이언트 연결 요청에 대한 정보는 시스템 내부적으로 관리되는 Queue에 쌓이게 되는데&lt;/b&gt;&lt;/span&gt;, 이 시점에서 클라이언트와의 연결은 아직 완전히 연결되지 않은 대기 상태이다. 대기 중인 연결 요청을 Queue로부터 꺼내와서 연결을 완료하기 위해서는 accept() API를 호출해야 한다.&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;  Accept&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;최종 연결 요청을 받아들이는 역할&lt;/blockquote&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: #ffffdd;&quot;&gt;&lt;b&gt;연결 요청을 Q로부터 꺼내와서 소켓 간 연결을 수립&lt;/b&gt;&lt;/span&gt;하는데, 이 때 데이터 통신을 위해 연결되는 소켓은 앞 과정에서 사용한 소켓이 아니라 accept() API 내부에서 새로 만들어진 소켓이다.&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: #ffffdd;&quot;&gt;&lt;b&gt;새로 만들어진 소켓과 포트 번호를 바인딩&lt;/b&gt;&lt;/span&gt;하고 클라이언트의 요청을 대기하기 위해 생성된 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;요청 대기 큐에 쌓여 있는 첫 번째 연결 요청이 매핑된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 서버 소켓의 역할이고, 서버 소켓의 남은 일은 다른 연결 요청을 처리하기 위해 다시 대기(listen)하거나 서버 소켓을 닫는(close) 것뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720416667710&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int accept(int sd, struct sockaddr *addr, int *addrlen);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;성공 시 새로운 socket descriptor 반환, 실패 시 -1 반환&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;sd&lt;/b&gt;: 생성된 소켓의 식별 번호&lt;/li&gt;
&lt;li&gt;&lt;b&gt;addr&lt;/b&gt;: accept 성공 시, 연결된 클라이언트의 주소 정보가 담기는 구조체&lt;/li&gt;
&lt;li&gt;&lt;b&gt;addrlen&lt;/b&gt;: sockaddr 구조체 크기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;반환 값은 socket 함수로 받은 듣기 소켓과는 전혀 다른 별개의 소켓이다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;듣기 소켓&lt;/b&gt;: 연결 요청을 확인하는(듣는) 역할만 수행, 연결 요청을 받으면 즉시 수신 대기열로 넘겨 다음 요청을 대기&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연결 소켓&lt;/b&gt;: 실제 클라이언트와 통신&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;즉, 연결을 받아들이는 과정과 통신하는 과정이 분리된다. 따라서, &lt;b&gt;듣기 소켓과 연결소켓의 구분을 통해, 통신하는 중에도 다른 클라이언트의 연결 요청을 받아들일 수 있다.&lt;/b&gt; &lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl5Jhk/btsItQAbRR8/L9vkUWtKvcKO10XuDXze61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl5Jhk/btsItQAbRR8/L9vkUWtKvcKO10XuDXze61/img.png&quot; data-alt=&quot;출처: https://codingfarm.tistory.com/538&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl5Jhk/btsItQAbRR8/L9vkUWtKvcKO10XuDXze61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl5Jhk%2FbtsItQAbRR8%2FL9vkUWtKvcKO10XuDXze61%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;779&quot; height=&quot;222&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://codingfarm.tistory.com/538&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; &amp;lt; socket() ~ accept()&amp;gt; server code&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;
&lt;pre id=&quot;code_1720417590155&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;netinet/in.h&amp;gt;
#define MYPORT 50000

int main()
{
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    int sin_size;
    
    sockfd = socket(PF_INET, SOCK_STREAM, 0); //socket()
    
    //setting server_addr
    memset(&amp;amp;server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(MYPORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    bind(sockfd, (struct sockaddr *)&amp;amp;server_addr, sizeof(server_addr)); //bind()
    
    listen(sockfd, 5); //listen()
    
    sin_size = sizeof(client_addr);
    
    //change a socket
    new_fd = accept(sockfd, (struct sockaddr *)&amp;amp;client_addr, &amp;amp;sin_size); //accept()
    return 0;
}​&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&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;  데이터 송수신 ( send() / recv() ) - TCP&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결된 소켓을 통해 데이터를 보낼 때는 send(), 데이터를 받을 때는 recv() API를 사용한다. 두 API도 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;Block 방식&lt;/b&gt;&lt;/span&gt;으로 동작한다. 그래서 0을 리턴하면 상대방 측에서 커넥션을 close 한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720417814669&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int send(int sockfd, const void *msg, int len, int flag)&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;write()와 동일 (flag는 보통 0으로 쓴다.)&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;u&gt;&lt;b&gt;성공 시 writing 한 바이트 수를 리턴, 실패 시 -1 리턴&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720417897415&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int recv(int sockfd, void *buf, int len, unsigned int flag)&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;read()와 동일 (flag는 보통 0으로 쓴다.)&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;u&gt;&lt;b&gt;성공 시 읽은 바이트 수를 리턴, 실패 시 -1 리턴&lt;/b&gt;&lt;/u&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;read &amp;harr; recv, write &amp;harr; send 사이에 차이는 없다. 자신의 OS에서 지원해 주는 함수를 사용하면 된다.&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;send()의 경우 데이터를 보내는 주체가 자기 자신이기 때문에 얼마만큼의 데이터를 보낼 것인지를 알 수 있다. 하지만 &lt;b&gt;데이터를 수신하는 경우 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;통신 대상이 언제, 어떤 데이터를 보낼 것인지를 특정할 수 없기 때문에 recv() API가 한번 실행되면 언제 끝날지 모르는 상태가 된다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;데이터 수신을 위한 recv() API는 별도의 스레드에서 실행&lt;/b&gt;&lt;/span&gt;한다. 소켓의 생성과 연결이 완료된 후, 새로운 스레드를 하나 만든 다음 그곳에서 recv()를 실행하고 데이터가 수신되길 기다리는 것이다.&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;  close and shutdown&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 송수신이 완료되고 더 이상 송수신이 필요 없게 되면 close() API를 통해 소켓을 닫는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 연결이 종료된 후 다시 데이터를 주고받고 싶다면 또 한 번의 소켓 생성과 연결 과정을 통해 소켓 데이터를 송수신할 수 있는 상태가 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720418540323&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int close(int sockfd)&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;TCP에서 클라이언트가 연결을 끊고 싶을 때 사용한다.&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;클라이언트가 close() 상태일 때&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 recv() ➡ return 0&lt;/li&gt;
&lt;li&gt;서버에서 send() ➡ return -1 (error)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Client error signal: SIGPIPE&lt;/li&gt;
&lt;li&gt;Server error signal: EPIPE&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720418778791&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int shutdown(int sockfd, int how)&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;연결을 끊는 것이 아닌 보내거나 받는 기능만 닫는 것이다. 이는 how에서 판단한다.&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;how에 올 수 있는 값은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb469o/btsIqDhAjJ3/U6dLdC4hXEyq4Fp3Dz1mk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb469o/btsIqDhAjJ3/U6dLdC4hXEyq4Fp3Dz1mk1/img.png&quot; data-alt=&quot;출처: https://seongmok.com/43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb469o/btsIqDhAjJ3/U6dLdC4hXEyq4Fp3Dz1mk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb469o%2FbtsIqDhAjJ3%2FU6dLdC4hXEyq4Fp3Dz1mk1%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;612&quot; height=&quot;83&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://seongmok.com/43&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;close()와 SHUT_RDWR의 차이는 소켓의 생존 여부다. close는 소켓 정보가 소멸하지만 SHUT_RDWR는 소켓 정보가 유지된다.&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;References:&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;a href=&quot;https://velog.io/@dltmdrl1244/%EC%86%8C%EC%BC%93%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D1-%EC%86%8C%EC%BC%93%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-%EA%B8%B0%EB%B3%B8-%EB%BC%88%EB%8C%80#%EC%86%8C%EC%BC%93%EC%9D%98-%EC%A3%BC%EC%86%8C-%ED%95%A0%EB%8B%B9-%EB%B0%8F-%EC%97%B0%EA%B2%B0--bind-&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@dltmdrl1244/%EC%86%8C%EC%BC%93%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D1-%EC%86%8C%EC%BC%93%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-%EA%B8%B0%EB%B3%B8-%EB%BC%88%EB%8C%80#%EC%86%8C%EC%BC%93%EC%9D%98-%EC%A3%BC%EC%86%8C-%ED%95%A0%EB%8B%B9-%EB%B0%8F-%EC%97%B0%EA%B2%B0--bind-&lt;/a&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;a href=&quot;https://velog.io/@dogfootbirdfoot/SocketProgramming#4-2-%EC%84%9C%EB%B2%84-%EC%86%8C%EC%BC%93-%EB%B0%94%EC%9D%B8%EB%94%A9--bind-&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@dogfootbirdfoot/SocketProgramming#4-2-%EC%84%9C%EB%B2%84-%EC%86%8C%EC%BC%93-%EB%B0%94%EC%9D%B8%EB%94%A9--bind-&lt;/a&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;a href=&quot;https://seongmok.com/43&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://seongmok.com/43&lt;/a&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;a href=&quot;http://jkkang.net/unix/netprg/chap2/net2_1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://jkkang.net/unix/netprg/chap2/net2_1.html&lt;/a&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;a href=&quot;https://blog.naver.com/lovinghc/30031089847&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/lovinghc/30031089847&lt;/a&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;a href=&quot;https://codingfarm.tistory.com/538&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://codingfarm.tistory.com/538&lt;/a&gt;&lt;/p&gt;</description>
      <category>Network</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/163</guid>
      <comments>https://dev-rootable.tistory.com/163#entry163comment</comments>
      <pubDate>Mon, 8 Jul 2024 15:11:01 +0900</pubDate>
    </item>
    <item>
      <title>C 계산기 구현</title>
      <link>https://dev-rootable.tistory.com/162</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  학습 목표&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;✔ 연결 리스트 자료 구조 사용&lt;br /&gt;✔ 구조체 노드 생성&lt;br /&gt;✔ 포인터를 통한 데이터 접근&lt;br /&gt;✔ 계산기 구현&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✍ 연결 리스트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;여러 데이터를 앞 뒤로 포인터를 통해 사슬처럼 연결할 수 있는 자료구조&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbMBv1/btsIoSyqcBf/dIHrIr54vjYQt59t1EEl51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbMBv1/btsIoSyqcBf/dIHrIr54vjYQt59t1EEl51/img.png&quot; data-alt=&quot;단순 연결 리스트(Singly Linked List)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbMBv1/btsIoSyqcBf/dIHrIr54vjYQt59t1EEl51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbMBv1%2FbtsIoSyqcBf%2FdIHrIr54vjYQt59t1EEl51%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;776&quot; height=&quot;145&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;단순 연결 리스트(Singly Linked List)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGK3fM/btsIoSd9n3W/V6CR7qCZVw4IlRhFQPL1xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGK3fM/btsIoSd9n3W/V6CR7qCZVw4IlRhFQPL1xk/img.png&quot; data-alt=&quot;이중 연결 리스트(Doubly Linked List)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGK3fM/btsIoSd9n3W/V6CR7qCZVw4IlRhFQPL1xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGK3fM%2FbtsIoSd9n3W%2FV6CR7qCZVw4IlRhFQPL1xk%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;789&quot; height=&quot;136&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이중 연결 리스트(Doubly Linked List)&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속되는 항목들이 포인터로 연결되어 있음&lt;/li&gt;
&lt;li&gt;마지막 항목은 NULL을 가리킴&lt;/li&gt;
&lt;li&gt;프로그램이 수행되는 동안 크기가 동적으로 변함&lt;/li&gt;
&lt;li&gt;메모리 공간 낭비가 적지만 포인터 메모리가 필요함&lt;/li&gt;
&lt;li&gt;배열보다 데이터 추가와 삭제, 삽입 등 용이&lt;/li&gt;
&lt;li&gt;순차 접근을 해기 때문에 탐색 속도가 떨어짐&lt;/li&gt;
&lt;li&gt;데이터는 객체 단위로 추가함&lt;/li&gt;
&lt;li&gt;head의 링크를 끊어버리면 전체 노드를 삭제할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  구현&lt;/b&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720157404292&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct
{
    double item;
    struct Node *ptr;
} Node;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 식으로 선언할 수 있다. 하지만 아직 메모리를 할당하지 않았기 때문에 실제로 생성되지는 않은 상태다.&lt;/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;✅ Head 선언&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 첫 번째 노드가 될 head 노드부터 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720157552395&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Node *head = NULL;&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720157609244&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;head = (Node *)malloc(sizeof(Node));
head-&amp;gt;item = 10;
head-&amp;gt;ptr = NULL;&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720158066519&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Node *node = NULL;
node = (Node *)malloc(sizeof(Node));
node-&amp;gt;item = 20;
node-&amp;gt;ptr = NULL;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720158089045&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;head-&amp;gt;ptr = node;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbXGR3/btsIn6kbOSH/FprEhxt4pskCelJ4j7ADY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbXGR3/btsIn6kbOSH/FprEhxt4pskCelJ4j7ADY1/img.png&quot; data-alt=&quot;현재 상태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbXGR3/btsIn6kbOSH/FprEhxt4pskCelJ4j7ADY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbXGR3%2FbtsIn6kbOSH%2FFprEhxt4pskCelJ4j7ADY1%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;558&quot; height=&quot;140&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 상태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✍ 구조체&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;다양한 데이터 조합을 지원하기 위해 변수들의 집합을 하나의 타입으로 나타낸 것&lt;/blockquote&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720158553110&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Product
{
    int id;
    char *name;
    int price;
};&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;또는 아래와 같이 익명으로 선언한 후 뒤에 typedef를 통해 별칭을 달아줄 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720158622920&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct
{
    int id;
    char *name;
    int price;
}Product;&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720159517355&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
    Product product1;

    // 초기화 - 방법 1
    product1.id = 1;
    product1.name = &quot;Server&quot;;
    product1.price = 50000;

    // 초기화 - 방법 2
    Product product2 = {.id = 1, .name = &quot;Server&quot;, .price = 50000};

    // 초기화 - 방법 3
    Product product3 = {1, &quot;Server&quot;, 50000};

    printf(&quot;id: %d\n&quot;, product1.id);
    printf(&quot;name: %s\n&quot;, product1.name);
    printf(&quot;price: %d\n&quot;, product1.price);

    return 0;
}&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조체 내 구조체를 선언할 수 있다. 본인이 설계하고자 하는 객체에 따라 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;  외부 참조 구조체&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720161315524&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct score
{
    double math;
    double english;
    double total;
    double average;
};

struct student
{
    int no;
    struct score s;
};

int main(void)
{
    struct student stu = {20170121, {90, 80, 0, 0}};

    stu.s.total = stu.s.math + stu.s.english;
    stu.s.average = stu.s.total / 2;

    printf(&quot;학번 : %d\n&quot;, stu.no);
    printf(&quot;총점 : %lf\n&quot;, stu.s.total);
    printf(&quot;평균 : %lf\n&quot;, stu.s.average);

    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;size18&quot;&gt;&lt;b&gt;  자기 참조 구조체&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720162049178&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

typedef struct student
{
    char name[20];
    int money;
    struct student *link;
} Student_List;

int main(void)
{
    Student_List stu1 = {&quot;박효원&quot;, 5000, NULL};
    Student_List stu2 = {&quot;라태인&quot;, 6000, NULL};
    Student_List stu3 = {&quot;강건욱&quot;, 4000, NULL};

    stu1.link = &amp;amp;stu2;
    stu2.link = &amp;amp;stu3;

    printf(&quot;%s %d\n&quot;, stu1.name, stu1.money);
    printf(&quot;%s %d\n&quot;, stu1.link-&amp;gt;name, stu1.link-&amp;gt;money);
    printf(&quot;%s %d\n&quot;, stu1.link-&amp;gt;link-&amp;gt;name, stu1.link-&amp;gt;link-&amp;gt;money);

    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;위 코드를 보면 stu1 -&amp;gt; stu2 -&amp;gt; stu3 식으로 참조를 할 수 있다. 이는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;연결 리스트와 유사&lt;/b&gt;&lt;/span&gt;한 구조를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  계산기&lt;/b&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 모든 입력 값은 노드이며, 노드들을 &lt;u&gt;연결리스트&lt;/u&gt;로 묶음&lt;br /&gt;2. &lt;u&gt;Stack 구조&lt;/u&gt;를 사용하므로, 연결리스트에 노드가 추가될 때마다 바로 앞 노드를 가리킨다.&lt;br /&gt;3. Stack 구조로 계산하기 위해서 입력된 수식을 &lt;u&gt;후위변환식&lt;/u&gt;으로 변경한다.&lt;br /&gt;4. 변환된 후위식에서 계산할 피연산자 단위를 구분하기 위해 &lt;u&gt;strtok&lt;/u&gt;을 구현한다.&lt;br /&gt;5. 계산을 통해 계산 결과를 출력한다. 이때, 소수점 3자리까지만 출력한다.&lt;/blockquote&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720164566296&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct
{
    double item;
    struct Node *link;
} Node;

typedef struct
{
    Node *head;
} LinkedList;&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;각 노드는 item이라는 값과 이전 노드를 가리키는 link를 가진다.&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;LlinkedList는 초기 head 역할을 하는 구조체로 head라는 포인터를 통해 노드를 연결하고 해제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 아래와 같이 초기화를 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720164746106&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void init(LinkedList *L)
{
    L-&amp;gt;head = NULL;
}&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720164767250&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int isEmpty(LinkedList *L)
{
    return (L-&amp;gt;head == NULL);
}

void push(LinkedList *L, double item)
{
    Node *node = (Node *)malloc(sizeof(Node)); // 새 노드 메모리 할당

    // 기존 노드와 연결
    node-&amp;gt;item = item;      // 노드 값 셋팅
    node-&amp;gt;link = (L-&amp;gt;head); // 새 노드 -&amp;gt; 기존 노드
    (L-&amp;gt;head) = node;       // 맨 앞 노드이므로 head로 변경
}

double pop(LinkedList *L)
{
    if (isEmpty(L))
    {
        printf(&quot;StackIsEmpty Error\n&quot;);
        exit(1);
    }
    else
    {
        Node *node = L-&amp;gt;head;     // node: 가장 최근에 연결된 노드
        double item = node-&amp;gt;item; // 반환할 값
        L-&amp;gt;head = L-&amp;gt;head-&amp;gt;link;  // 바로 앞에 위치한 노드가 head가 됨
        free(node);
        return item;
    }
}

double peek(LinkedList *L)
{
    if (isEmpty(L))
    {
        printf(&quot;StackIsEmpty Error\n&quot;);
        exit(1);
    }
    else
    {
        return L-&amp;gt;head-&amp;gt;item; // 가장 뒷 노드의 값 리턴
    }
}&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;push를 하게 되면 아래와 같은 그림이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNSd1a/btsIoR7KZeC/JLtB48XOTKBonzLFMkOiM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNSd1a/btsIoR7KZeC/JLtB48XOTKBonzLFMkOiM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNSd1a/btsIoR7KZeC/JLtB48XOTKBonzLFMkOiM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNSd1a%2FbtsIoR7KZeC%2FJLtB48XOTKBonzLFMkOiM1%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;344&quot; height=&quot;332&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;332&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;push 과정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 새 노드를 생성(메모리 할당)한다.&lt;br /&gt;- 새 노드에 값을 세팅&lt;br /&gt;- 새 노드는 head(앞 노드)를 가리킨다.&lt;br /&gt;- 새 노드가 새로운 head가 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop 은 아래와 같이 동작한다. head를 통해 새 노드가 연결되기 때문에 head를 변경하고 메모리를 해제해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbrLBb/btsIpMkj70B/uwsigDkCZ7LbljEkHcQku1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbrLBb/btsIpMkj70B/uwsigDkCZ7LbljEkHcQku1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbrLBb/btsIpMkj70B/uwsigDkCZ7LbljEkHcQku1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbrLBb%2FbtsIpMkj70B%2FuwsigDkCZ7LbljEkHcQku1%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;312&quot; height=&quot;296&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;296&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;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;pop 과정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 기존 head의 값과 주소 저장&lt;br /&gt;- head를 앞 노드로 변경&lt;br /&gt;- 메모리 해제&lt;br /&gt;- 값 반환&lt;/blockquote&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720165675193&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void toPostfix(char exp[], char postfix[])
{
    int p = 0; // p : postfix 배열 인덱스
    int len = cstrlen(exp);
    char ch;
    Node *node = NULL;

    init(&amp;amp;node);

    for (int i = 0; i &amp;lt; len; i++)
    {
        ch = exp[i];

        switch (ch)
        {
        case '*':
        case '/':
        case '+':
        case '-':
            while (!isEmpty(&amp;amp;node) &amp;amp;&amp;amp; priority(ch) &amp;lt;= priority(peek(&amp;amp;node)))
            {
                postfix[p++] = ' ';
                postfix[p++] = pop(&amp;amp;node);
            }
            push(&amp;amp;node, ch);
            break;
        case '(':
            push(&amp;amp;node, ch);
            break;
        case ')':
            while (!isEmpty(&amp;amp;node) &amp;amp;&amp;amp; peek(&amp;amp;node) != '(') // find '('
            {
                postfix[p++] = ' ';
                postfix[p++] = pop(&amp;amp;node);
            }
            pop(&amp;amp;node); //'(' 제거
            break;
        default:                              // digit
            if (i &amp;gt; 0 &amp;amp;&amp;amp; exp[i - 1] - 48 &amp;lt; 0) // 수식 숫자 앞 연산자 -&amp;gt; 공백
            {
                postfix[p++] = ' ';
            }
            postfix[p++] = ch;
            break;
        }
    }

    while (!isEmpty(&amp;amp;node))
    {
        postfix[p++] = ' ';
        postfix[p++] = pop(&amp;amp;node);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 문자 배열로 받은 수식을 하나씩 처리하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식은 크게 연산자, 피연산자, 괄호로 나누어 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후위표현식을 만드는 규칙은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;모든 값은 공백으로 구분&lt;/b&gt;&lt;/u&gt;할 것이기 때문에 후위식 배열(postfix)에 넣기 전에 추가한다.&lt;/li&gt;
&lt;li&gt;모든 숫자는 바로 후위식 배열에 담는다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;딘, 2자리 이상 숫자를 처리하기 위해 수식의 숫자 이전 자리에 연산자가 있을 때만 공백을 넣는다.&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;'('는 스택(연결리스트)에 담는다.&lt;/li&gt;
&lt;li&gt;사칙 연산자는 스택이 비지 않았고, 현재 연산자 우선순위가 앞에 들어간 연산자보다 같거나 낮을 경우 앞에 들어간 연산자를 후위식 배열에 추가하고 현재 연산자를 스택에 추가한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;연산자 우선순위가 높은 것이 먼저 계산되어야 하기 때문&lt;/b&gt;에 후위식 배열에 먼저 들어간 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;')' 연산자는 '(' 연산자를 찾을 때까지 스택을 탐색하면서 연산자를 후위식 배열로 옮긴다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;'('을 제거해야 하기 때문에 탐색 후 pop을 한번 더 수행&lt;/b&gt;&lt;/u&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수식의 마지막은 숫자다. 그래서 &lt;u&gt;&lt;b&gt;처리가 끝난 후에 스택에 연산자가 반드시 남는다.&lt;/b&gt;&lt;/u&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;반복문을 통해 남은 연산자를 후위식 배열로 옮긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720166505500&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int cstrlen(char *str)
{
    int idx = 0;
    while (str[idx] != '\0')
        ++idx;
    return idx;
}

char *cstrtok(char *postfix, char *delim)
{
    char *start = 0; // 문자열 시작 위치
    int i = 0;
    static char *tmp; // 문자열 주소 저장

    if (postfix != NULL) // 첫 토큰 받을 때
        start = postfix;
    else // 두 번째 이후 토큰 받을 때
        start = tmp;

    if (cstrlen(start) &amp;lt; 1) // 문자열 종료
        return NULL;

    for (i = 0; i &amp;lt; cstrlen(start); i++)
    {
        if (start[i] == (*delim) || start[i] == '\0')
        {
            start[i] = '\0';
            break;
        }
    }

    tmp = &amp;amp;start[i + 1]; // 다음 배열 시작 위치

    return start;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cstrtok은 strtok을 구현한 것이다.&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;strtok과 같이 동작하도록 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;처음 호출했을 때(postfix != NULL)는 첫 문자열을 가리키도록&lt;/b&gt;&lt;/span&gt; 하고, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;공백 또는 NULL을 찾을 때마다 NULL로 대체&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;start라는 포인터를 이동하면서 공백을 기준으로 나누어진 토큰을 가리킨다.&lt;/b&gt;&lt;/span&gt; tmp는 다음 토큰의 포인터를 저장하기 위한 임시 포인터다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720166455908&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double calculate(char postfix[])
{
    int len = cstrlen(postfix);
    double op1 = 0.0, op2 = 0.0;
    Node *node = NULL;
    char *delim = &quot; &quot;;
    char *token;

    init(&amp;amp;node);

    token = cstrtok(postfix, delim);

    while (token != NULL)
    {
        printf(&quot;token: %s\n&quot;, token);

        if (token[0] != '*' &amp;amp;&amp;amp; token[0] != '/' &amp;amp;&amp;amp; token[0] != '+' &amp;amp;&amp;amp; token[0] != '-')
        {
            int i = 0;
            double val = 0.0; // 두자리 수 이상 결과

            // 각 토큰 -&amp;gt; 스택 저장
            while (token[i] != '\0')
            {
                val = val * 10 + (token[i] - 48);
                i++;
            }

            push(&amp;amp;node, val);
        }

        else
        {
            op2 = pop(&amp;amp;node);
            op1 = pop(&amp;amp;node);

            switch (token[0])
            {
            case '+':
                push(&amp;amp;node, op1 + op2);
                break;
            case '-':
                push(&amp;amp;node, op1 - op2);
                break;
            case '*':
                push(&amp;amp;node, op1 * op2);
                break;
            case '/':
                if (op2 == 0) // 나누기 0 예외 처리
                    printf(&quot;DivideByZeroException\n&quot;);
                push(&amp;amp;node, op1 / op2);
                break;
            }
        }

        token = cstrtok(NULL, delim);
    }

    return pop(&amp;amp;node);
}&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;변환된 후위식은 공백을 기준으로 연산자와 피연산자가 들어 있다. 여기서 공백을 제거한 연산자와 피연산자만을 가지는 포인터가 token이다.&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;후위식 계산은 숫자일 때는 스택에 넣고, 연산자가 나오면 숫자를 2개 pop()하여 해당 연산자에 맞는 작업을 하면 된다. 여기서 0으로 나누는 경우를 예외 처리했고, 2자리 이상 숫자일 경우 해당 숫자를 만들도록 했다.&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720167103616&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

typedef struct
{
    double item;
    struct Node *link;
} Node;

typedef struct
{
    Node *head;
} LinkedList;

void init(LinkedList *L)
{
    L-&amp;gt;head = NULL;
}

int isEmpty(LinkedList *L)
{
    return (L-&amp;gt;head == NULL);
}

void push(LinkedList *L, double item)
{
    Node *node = (Node *)malloc(sizeof(Node)); // 새 노드 메모리 할당

    // 기존 노드와 연결
    node-&amp;gt;item = item;      // 노드 값 셋팅
    node-&amp;gt;link = (L-&amp;gt;head); // 새 노드 -&amp;gt; 기존 노드
    (L-&amp;gt;head) = node;       // 맨 앞 노드이므로 head로 변경
}

double pop(LinkedList *L)
{
    if (isEmpty(L))
    {
        printf(&quot;StackIsEmpty Error\n&quot;);
        exit(1);
    }
    else
    {
        Node *node = L-&amp;gt;head;     // node: 가장 최근에 연결된 노드
        double item = node-&amp;gt;item; // 반환할 값
        L-&amp;gt;head = L-&amp;gt;head-&amp;gt;link;  // 바로 앞에 위치한 노드가 head가 됨
        free(node);
        return item;
    }
}

double peek(LinkedList *L)
{
    if (isEmpty(L))
    {
        printf(&quot;StackIsEmpty Error\n&quot;);
        exit(1);
    }
    else
    {
        return L-&amp;gt;head-&amp;gt;item; // 가장 뒷 노드의 값 리턴
    }
}

int priority(char op)
{
    switch (op)
    {
    case '(':
    case ')':
        return 0;
    case '+':
    case '-':
        return 1;
    case '*':
    case '/':
        return 2;
    }
    return -1;
}

int cstrlen(char *str)
{
    int idx = 0;
    while (str[idx] != '\0')
        ++idx;
    return idx;
}

char *cstrtok(char *postfix, char *delim)
{
    char *start = 0; // 문자열 시작 위치
    int i = 0;
    static char *tmp; // 문자열 주소 저장

    if (postfix != NULL) // 첫 토큰 받을 때
        start = postfix;
    else // 두 번째 이후 토큰 받을 때
        start = tmp;

    if (cstrlen(start) &amp;lt; 1) // 문자열 종료
        return NULL;

    for (i = 0; i &amp;lt; cstrlen(start); i++)
    {
        if (start[i] == (*delim) || start[i] == '\0')
        {
            start[i] = '\0';
            break;
        }
    }

    tmp = &amp;amp;start[i + 1]; // 다음 배열 시작 위치

    return start;
}

double cround(double num, int place)
{
    for (int i = 1; i &amp;lt; place; i++)
    {
        num *= 10;
    }

    num += 0.5;

    for (int i = 1; i &amp;lt; place; i++)
    {
        num /= 10;
    }

    return num;
}

void toPostfix(char exp[], char postfix[])
{
    int p = 0; // p : postfix 배열 인덱스
    int len = cstrlen(exp);
    char ch;
    Node *node = NULL;

    init(&amp;amp;node);

    for (int i = 0; i &amp;lt; len; i++)
    {
        ch = exp[i];

        switch (ch)
        {
        case '*':
        case '/':
        case '+':
        case '-':
            while (!isEmpty(&amp;amp;node) &amp;amp;&amp;amp; priority(ch) &amp;lt;= priority(peek(&amp;amp;node)))
            {
                postfix[p++] = ' ';
                postfix[p++] = pop(&amp;amp;node);
            }
            push(&amp;amp;node, ch);
            break;
        case '(':
            push(&amp;amp;node, ch);
            break;
        case ')':
            while (!isEmpty(&amp;amp;node) &amp;amp;&amp;amp; peek(&amp;amp;node) != '(') // find '('
            {
                postfix[p++] = ' ';
                postfix[p++] = pop(&amp;amp;node);
            }
            pop(&amp;amp;node); //'(' 제거
            break;
        default:                              // digit
            if (i &amp;gt; 0 &amp;amp;&amp;amp; exp[i - 1] - 48 &amp;lt; 0) // 수식 숫자 앞 연산자 -&amp;gt; 공백
            {
                postfix[p++] = ' ';
            }
            postfix[p++] = ch;
            break;
        }
    }

    while (!isEmpty(&amp;amp;node))
    {
        postfix[p++] = ' ';
        postfix[p++] = pop(&amp;amp;node);
    }
}

double calculate(char postfix[])
{
    int len = cstrlen(postfix);
    double op1 = 0.0, op2 = 0.0;
    Node *node = NULL;
    char *delim = &quot; &quot;;
    char *token;

    init(&amp;amp;node);

    token = cstrtok(postfix, delim);

    while (token != NULL)
    {
        printf(&quot;token: %s\n&quot;, token);

        if (token[0] != '*' &amp;amp;&amp;amp; token[0] != '/' &amp;amp;&amp;amp; token[0] != '+' &amp;amp;&amp;amp; token[0] != '-')
        {
            int i = 0;
            double val = 0.0; // 두자리 수 이상 결과

            // 각 토큰 -&amp;gt; 스택 저장
            while (token[i] != '\0')
            {
                val = val * 10 + (token[i] - 48);
                i++;
            }

            push(&amp;amp;node, val);
        }

        else
        {
            op2 = pop(&amp;amp;node);
            op1 = pop(&amp;amp;node);

            switch (token[0])
            {
            case '+':
                push(&amp;amp;node, op1 + op2);
                break;
            case '-':
                push(&amp;amp;node, op1 - op2);
                break;
            case '*':
                push(&amp;amp;node, op1 * op2);
                break;
            case '/':
                if (op2 == 0) // 나누기 0 예외 처리
                    printf(&quot;DivideByZeroException\n&quot;);
                push(&amp;amp;node, op1 / op2);
                break;
            }
        }

        token = cstrtok(NULL, delim);
    }

    return pop(&amp;amp;node);
}

int main(void)
{
    int n = 0;           // 반올림 자리
    double result = 0.0; // 계산 결과
    char *exp;           // 입력
    char postfix[MAX_SIZE] = {'\0'};
    exp = (char *)malloc(sizeof(char) * 100);

    printf(&quot;계산식을 입력한 후 Enter를 눌러주세요!\n&quot;);
    scanf(&quot;%s&quot;, exp);

    toPostfix(exp, postfix);
    printf(&quot;\nPostfix: %s\n&quot;, postfix);

    printf(&quot;몇 번째 자리에서 반올림하시겠습니까? &amp;gt;&amp;gt;&amp;gt;&quot;);
    scanf(&quot;%d&quot;, &amp;amp;n);

    result = calculate(postfix);

    printf(&quot;계산한 결과: \t%.3f\n&quot;, cround(result, n));

    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;&lt;b&gt;References:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@riceintheramen/Linked-list#%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@riceintheramen/Linked-list#%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%9D%98-%ED%8A%B9%EC%A7%95&lt;/a&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;a href=&quot;https://velog.io/@kysung95/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%A1%A0-C%EB%A1%9C-%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@kysung95/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%A1%A0-C%EB%A1%9C-%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90&lt;/a&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;a href=&quot;https://www.tcpschool.com/c/c_struct_intro&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.tcpschool.com/c/c_struct_intro&lt;/a&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;a href=&quot;https://wikidocs.net/12623&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/12623&lt;/a&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;a href=&quot;https://wikidocs.net/12625&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/12625&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/162</guid>
      <comments>https://dev-rootable.tistory.com/162#entry162comment</comments>
      <pubDate>Fri, 5 Jul 2024 17:12:19 +0900</pubDate>
    </item>
    <item>
      <title>왜 C언어를 사용하는가</title>
      <link>https://dev-rootable.tistory.com/161</link>
      <description>&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  C언어의 주요 기능&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  저수준 메모리 접근&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;포인터를 통해 메모리 할당, 해제 및 데이터 조작&lt;/b&gt;&lt;/span&gt; 등의 작업을 수행할 수 있다. 이 기능은 메모리 관리가 중요한 시스템 프로그래밍과 임베디드 시스템 개발에서 큰 장점으로 작용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  다양한 표준 라이브러리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C 언어는 표준 입출력, 문자열 처리, 수학 연산, 파일 처리 등 다양한 기본적인 프로그래밍 작업을 지원하는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;방대한 표준 라이브러리를 제공&lt;/b&gt;&lt;/span&gt;한다. 이것은 C 언어가 저수준뿐만 아니라 &lt;b&gt;고수준 언어의 특징을 갖고 있어 다양한 애플리케이션 개발을 지원&lt;/b&gt;한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  이식성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;C언어의 표준화 덕분에, 소스 코드는 매우 적은 변경이나 추가 작업 없이도 다른 시스템으로 옮겨져 컴파일될 수 있다.&lt;/b&gt; 따라서, C언어로 작성된 프로그램은 다양한 플랫폼과 하드웨어에서 실행될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  구조체를 통한 복잡한 데이터 구조의 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어는 &lt;b&gt;구조체(struct)를 사용하여 사용자 정의 데이터 타입을 생성할 수 있다.&lt;/b&gt; 이를 통해 복잡한 데이터 구조를 효율적으로 표현할 수 있어 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;코드 가독성&lt;/b&gt;&lt;/span&gt;을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  코드 재사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어는 &lt;b&gt;함수를 통해 코드를 모듈 단위로 나눌 수 있다.&lt;/b&gt; 이를 통해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;코드의 재사용성이 증가하고 유지 보수가 용이해진다.&lt;/b&gt;&lt;/span&gt; 또한, 헤더 파일을 통해 함수 선언과 정의를 분리할 수 있어, 대규모 프로젝트의 관리가 더욱 효율적으로 이루어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 C언어의 특징을 이렇게 요약하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;저수준 언어와 고수준 언어의 특징을 모두 가진 언어&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  C언어 처리 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7Boh3/btsIktdGUNO/NBKChhEM2HRj9Ns7aTWWJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7Boh3/btsIktdGUNO/NBKChhEM2HRj9Ns7aTWWJ0/img.png&quot; data-alt=&quot;출처: https://gracefulprograming.tistory.com/16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7Boh3/btsIktdGUNO/NBKChhEM2HRj9Ns7aTWWJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7Boh3%2FbtsIktdGUNO%2FNBKChhEM2HRj9Ns7aTWWJ0%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;336&quot; height=&quot;419&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://gracefulprograming.tistory.com/16&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  전처리 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리 과정은 크게 두 부분으로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;헤더 파일 삽입&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;매크로 치환 및 적용&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#define 된 부분은 심볼 테이블에 저장되고, 심볼 테이블에 들어 있는 문자열과 같은 문자열을 만나면 #define 된 내용으로 치환한다. 이때 #ifdef와 같은 다른 전처리기 매크로들도 같이 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  컴파일 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 과정은 크게 전단부, 중단부, 후단부로 나눌 수 있다. &lt;b&gt;컴파일 결과로 .s 어셈블리 코드로 이루어진 파일이 만들어진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✔ 전단부&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전단부에서는 소스코드가 올바르게 작성되었는지 분석하고, 중단부에 넘겨주기 위한 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;GIMPLE 트리&lt;/b&gt;&lt;/span&gt;를 생성하는 일을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;GIMPLE 트리 : 소스 코드를 트리 형태로 표현한 자료 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;과정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 어휘 분석&lt;/b&gt;: C 소스코드를 의미 있는 최소단위(토큰)로 나눈다.&lt;br /&gt;&lt;b&gt;2. 구문 분석&lt;/b&gt;: 토큰으로 Parse Tree를 만들면서 문법적 오류 검출&lt;br /&gt;&lt;b&gt;3. 의미 분석&lt;/b&gt;: Parse Tree를 이용해 문법적 오류는 없지만 의미상 오류가 있는지 검사 &lt;br /&gt;(함수 매개변수 잘못 사용, 변수 자료형 불일치 등)&lt;br /&gt;&lt;b&gt;4. 중간 표현 생성&lt;/b&gt;: 언어 독립적인 특성을 제공하기 위해 트리 형태의 중간 표현(GIMPLE Tree)을 생성&lt;/blockquote&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;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전단부에서 넘겨받은 GIMPLE 트리를 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;SSA(Static Single Assignment) 형태로 변환&lt;/b&gt;&lt;/span&gt;한 후 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;아키텍쳐 비종속적인 최적화&lt;/b&gt;&lt;/span&gt;를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;SSA(Static Single Assignment)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;각 변수가 정확히 한 번만 할당되는 중간 표현(IR) 유형&lt;/b&gt;&lt;/span&gt;을 말한다. 많은 상용 컴파일러를 포함하여 명령형 언어에 대한 대부분의 고품질 최적화 컴파일러에서 사용된다.&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;298&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y8FZt/btsIiTq9YwV/pTKWckpJryTcCmVsaxTzkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y8FZt/btsIiTq9YwV/pTKWckpJryTcCmVsaxTzkK/img.png&quot; data-alt=&quot;SSA 변환&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y8FZt/btsIiTq9YwV/pTKWckpJryTcCmVsaxTzkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy8FZt%2FbtsIiTq9YwV%2FpTKWckpJryTcCmVsaxTzkK%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;298&quot; height=&quot;310&quot; data-origin-width=&quot;298&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSA 변환&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzwo5u/btsIifOZdWm/hzZY3Dy4lok337IDnEH9Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzwo5u/btsIifOZdWm/hzZY3Dy4lok337IDnEH9Gk/img.png&quot; data-alt=&quot;변수마다 버전 부여&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzwo5u/btsIifOZdWm/hzZY3Dy4lok337IDnEH9Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbzwo5u%2FbtsIifOZdWm%2FhzZY3Dy4lok337IDnEH9Gk%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;236&quot; height=&quot;304&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;변수마다 버전 부여&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1cMU0/btsIixIBVYS/uOvx3Sgl59x9nKrQY1CarK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1cMU0/btsIixIBVYS/uOvx3Sgl59x9nKrQY1CarK/img.png&quot; data-alt=&quot;&amp;amp;Phi; 함수를 통해 재할당&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1cMU0/btsIixIBVYS/uOvx3Sgl59x9nKrQY1CarK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1cMU0%2FbtsIixIBVYS%2FuOvx3Sgl59x9nKrQY1CarK%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;245&quot; height=&quot;309&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&amp;Phi; 함수를 통해 재할당&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;br /&gt;&lt;b&gt;Reference:&lt;/b&gt; &lt;br /&gt;https://en.wikipedia.org/wiki/Static_single-assignment_form&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아키텍처 비종속적인 최적화란 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;서로 다른 CPU 아키텍처에 구애받지 않고 공통적으로 수행할 수 있는 최적화&lt;/b&gt;&lt;/span&gt;를 말한다. 중단부에서는 SSA 기반으로 최적화를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;✔ 후단부&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;후반부에서는 RTL Optimizer에 의해 아키텍처 비종속적인 최적화와 함께 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;아키텍처 종속적인 최적화&lt;/b&gt;&lt;/span&gt;를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아키텍처 종속적인 최적화는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;각 프로그램 내의 명령어 중 아키텍처별로 좀 더 효율적인 명령어로 대체해 성능을 높이는 작업&lt;/b&gt;&lt;/span&gt;과 같이 아키텍쳐 특성에 따라 최적화를 수행하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  어셈블 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일이 끝난 어셈블리 코드는 어셈블러에 의해 기계어로 어셈블 된다.&lt;/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;어셈블러에 의해 생성되는 목적코드(.o) 파일은 어셈블 된 프로그램의 명령어와 데이터가 들어있는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;ELF 바이너리 포맷(Binary Format) 구조&lt;/b&gt;&lt;/span&gt;를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계인 링킹에서 링커가 바이너리 파일을 하나의 실행 파일로 묶기 위해서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;각 바이너리의 정보를 효과적으로 파악하기 위해서(명령어와 데이터의 범위 등) 일정한 규칙을 갖게 형식화해놓은 것&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;링커는 오브젝트 파일들과 프로그램에서 사용된 표준 C 라이브러리, 사용자 라이브러리를 링크(Link)&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 링킹 과정이 끝나면 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;실행 가능한 실행 파일&lt;/b&gt;&lt;/span&gt;이 만들어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  C언어를 사용하는 목적&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✍ Instuction code를 생성할 수 있는 언어&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 아래와 같은 자신이 이해할 수 있는 포맷이 있는데 이를 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;Instruction Format&lt;/b&gt;&lt;/span&gt;이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crLgLo/btsIiP97qzw/aWpHMbbrLIC5K2xKBSdD2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crLgLo/btsIiP97qzw/aWpHMbbrLIC5K2xKBSdD2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crLgLo/btsIiP97qzw/aWpHMbbrLIC5K2xKBSdD2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrLgLo%2FbtsIiP97qzw%2FaWpHMbbrLIC5K2xKBSdD2k%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;737&quot; height=&quot;473&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;691&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;메모리를 직접 제어&lt;/b&gt;할 수 있으며, 컴파일 결과 CPU가 직접 이해할 수 있는 &lt;b&gt;Instruction code를 생성&lt;/b&gt;할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어는&lt;b&gt; 포인터를 통해 메모리를 직접 제어&lt;/b&gt;할 수 있다는 점, &lt;b&gt;컴파일 결과 중간 코드 없이 바로 바이너리 코드를 생성&lt;/b&gt;할 수 있다는 점에서 시스템 제어와 효율성이 중요한 분야에서 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719887722559&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void swap(int *xp, int *yp)
{
    int t0 = *xp;
    int t1 = *yp;
    *xp = t1;
    *yp = t0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OANGk/btsIkq2vaae/f8S6ftjX9bN4b8X925BBA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OANGk/btsIkq2vaae/f8S6ftjX9bN4b8X925BBA1/img.png&quot; data-alt=&quot;어셈블러 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OANGk/btsIkq2vaae/f8S6ftjX9bN4b8X925BBA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOANGk%2FbtsIkq2vaae%2Ff8S6ftjX9bN4b8X925BBA1%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;397&quot; height=&quot;118&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;어셈블러 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✍ 포인터&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;데이터가 저장된 메모리의 주소값을 저장하는 변수&lt;br /&gt;즉, &lt;b&gt;프로그래머가 컴퓨터 메모리에 직접 접근해서 제어할 수 있게 하는 도구&lt;/b&gt;&lt;/blockquote&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719824281773&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main(void)
{
    int val1 = 10;
    int val2 = 20;
    int val3 = 30;

    printf(&quot;메모리상 val1 위치 : %d\n&quot;, &amp;amp;val1);
    printf(&quot;메모리상 val2 위치 : %d\n&quot;, &amp;amp;val2);
    printf(&quot;메모리상 val3 위치 : %d\n&quot;, &amp;amp;val3);

    int *cursor = &amp;amp;val1;

    //포인터 변수 cursor를 통해 val1 접근
    printf(&quot;val1의 주소: %d, 값: %d\n&quot;, &amp;amp;val1, val1);
    printf(&quot;cursor의 주소: %d, 값: %d\n&quot;, cursor, *cursor);

    *cursor = *cursor + 5;

    //포인터 변수는 가리키는 값을 공유한다.
    printf(&quot;val1의 주소: %d, 값: %d\n&quot;, &amp;amp;val1, val1);
    printf(&quot;cursor의 주소: %d, 값: %d\n&quot;, cursor, *cursor);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8YnRQ/btsIi9NTffz/rSjZkADvMethhTYtpowtKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8YnRQ/btsIi9NTffz/rSjZkADvMethhTYtpowtKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8YnRQ/btsIi9NTffz/rSjZkADvMethhTYtpowtKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8YnRQ%2FbtsIi9NTffz%2FrSjZkADvMethhTYtpowtKK%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;243&quot; height=&quot;154&quot; data-origin-width=&quot;243&quot; data-origin-height=&quot;154&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;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  포인터는 접근한 값을 저장할 수 있다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719824659689&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#define SIZE 1000

void swap(int *a, int *b);
void quickSort(int start, int end);

int a[SIZE];

int main(void)
{
    int size;
    printf(&quot;Array size : &quot;);
    scanf(&quot;%d&quot;, &amp;amp;size);
    
    //배열 값 입력
    printf(&quot;Input array values &amp;gt;&amp;gt; &quot;);
    for (int i = 0; i &amp;lt; size; i++) {
        scanf(&quot;%d&quot;, &amp;amp;a[i]);
    }
    
    quickSort(0, size - 1);

    printf(&quot;Result: &quot;);
    for (int i = 0; i &amp;lt; size; i++)
    {
        printf(&quot;%d &quot;, a[i]);
    }
    
    return 0;
}

void quickSort(int start, int end) {
    if (start &amp;gt;= end) return;

    int key = start, i = start + 1, j = end; //가장 좌측 값을 키로
    
    while (i &amp;lt;= j) {
        while (i &amp;lt;= end &amp;amp;&amp;amp; a[i] &amp;lt;= a[key]) i++;
        while (j &amp;gt; start &amp;amp;&amp;amp; a[j] &amp;gt;= a[key]) j--;
        
        if (i &amp;gt; j) swap(&amp;amp;a[key], &amp;amp;a[j]);
        else swap(&amp;amp;a[i], &amp;amp;a[j]);
    }
    
    quickSort(start, j - 1);
    quickSort(j + 1, end);
 }

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxgaW4/btsIkqIqsmv/U9FADUtDfrcPJkJ0hFt851/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxgaW4/btsIkqIqsmv/U9FADUtDfrcPJkJ0hFt851/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxgaW4/btsIkqIqsmv/U9FADUtDfrcPJkJ0hFt851/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxgaW4%2FbtsIkqIqsmv%2FU9FADUtDfrcPJkJ0hFt851%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;307&quot; height=&quot;53&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;53&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;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  포인터는 자료구조의 가장 앞 부분을 가리킨다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719899467982&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

struct Student
{
    int id;     // 학번
    int grade;  // 학년
    int total;  // 총학점
    char *name; // 이름
};

int main(void)
{
    struct Student student[3] = {{1, 3, 88, &quot;Mike&quot;}, {2, 4, 116, &quot;Tony&quot;}, {3, 1, 38, &quot;Justin&quot;}};
    struct Student *ptr;
    ptr = &amp;amp;student;

    printf(&quot; **** Student Info ****\n&quot;);
    for (int i = 0; i &amp;lt; 3; i++)
    {
        printf(&quot;\nID: %d\n&quot;, ptr-&amp;gt;id);
        printf(&quot;Grade: %d\n&quot;, ptr-&amp;gt;grade);
        printf(&quot;Total score: %d\n&quot;, ptr-&amp;gt;total);
        printf(&quot;Name: %s\n&quot;, ptr-&amp;gt;name);
        printf(&quot;------------------------------&quot;);
        ptr++; // 구조체 시작 부분을 가리킴
    }

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMFOx/btsIlGcJUhL/7NehC2YCzGmjE4Vmo0MzE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMFOx/btsIlGcJUhL/7NehC2YCzGmjE4Vmo0MzE1/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMFOx/btsIlGcJUhL/7NehC2YCzGmjE4Vmo0MzE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMFOx%2FbtsIlGcJUhL%2F7NehC2YCzGmjE4Vmo0MzE1%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;422&quot; height=&quot;317&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5LGki/btsIkGYT5kk/jekTjaQyeAhi9rAj8EK4z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5LGki/btsIkGYT5kk/jekTjaQyeAhi9rAj8EK4z0/img.png&quot; data-alt=&quot;도식화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5LGki/btsIkGYT5kk/jekTjaQyeAhi9rAj8EK4z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5LGki%2FbtsIkGYT5kk%2FjekTjaQyeAhi9rAj8EK4z0%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;763&quot; height=&quot;306&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도식화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;References:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://it-stargazer.com/c%EC%96%B8%EC%96%B4-%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%EC%9D%98-%EC%9D%B4%EC%9C%A0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://it-stargazer.com/c%EC%96%B8%EC%96%B4-%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%EC%9D%98-%EC%9D%B4%EC%9C%A0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8/&lt;/a&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;a href=&quot;https://lacommune.tistory.com/76&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lacommune.tistory.com/76&lt;/a&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;a href=&quot;https://www.learncomputerscienceonline.com/instruction-cycle/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.learncomputerscienceonline.com/instruction-cycle/&lt;/a&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;a href=&quot;https://suhwanc.tistory.com/m/134&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://suhwanc.tistory.com/m/134&lt;/a&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;a href=&quot;https://gracefulprograming.tistory.com/16&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gracefulprograming.tistory.com/16&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>C</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/161</guid>
      <comments>https://dev-rootable.tistory.com/161#entry161comment</comments>
      <pubDate>Mon, 1 Jul 2024 15:22:57 +0900</pubDate>
    </item>
    <item>
      <title>SSR에 JWT 적용이 부적합한 이유</title>
      <link>https://dev-rootable.tistory.com/160</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;세션 방식으로 구현했던 SSR 서버에 JWT를 적용하면서 느낀 점을 정리하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;/h3&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;  Stateless 하지 않다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR 서버는 매 요청마다 비즈니스 로직을 수행하고 컨트롤러를 통해 렌더링 한다. 이것은 &lt;u&gt;&lt;b&gt;매 요청마다 인증 토큰을 생성한다&lt;/b&gt;&lt;/u&gt;고 해석할 수 있다. &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;Stateless 특징을 가지는 JWT를 사용할 때 서버의 발급은 1회고, 그 이후에는 검증만 수행한다.&lt;/b&gt;&lt;/span&gt; 결과적으로 &lt;u&gt;&lt;b&gt;Stateless 하지 않다고 볼 수 있다.&lt;/b&gt;&lt;/u&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;JWT 방식은 클라이언트 스토리지에 저장되기 때문에 쿠키를 통해 구현할 수 있다. 아래 코드는 세션 방식과 쿠키를 통해 JWT를 구현한 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717829168680&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @PostMapping(&quot;/login&quot;)
    public String login(@Valid @ModelAttribute(&quot;loginForm&quot;) LoginDto dto, BindingResult bindingResult,
                        @RequestParam(defaultValue = &quot;/&quot;) String redirectURL) {

        log.info(&quot;로그인 검증&quot;);

        if (bindingResult.hasErrors()) {
            log.info(&quot;검증 에러 errors={}&quot;, bindingResult);
            return &quot;login/signIn&quot;;
        }

        //로그인 유효성 검사
        Member loginMember = loginService.validationLogin(dto.getLoginId(), dto.getPassword());
        log.info(&quot;Login Member = {}&quot;, loginMember);

        if (loginMember == null) {
            bindingResult.reject(&quot;loginFail&quot;);
            return &quot;login/signIn&quot;;
        }

        //로그인 성공 처리
        log.info(&quot;정상 입력으로 로그인 성공&quot;);

        httpSession.setAttribute(&quot;loginMember&quot;, new SessionMember(loginMember)); //세션에 회원 정보 저장


        return &quot;redirect:&quot; + redirectURL; //로그인 후 최초 위치로 돌아가도록

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 세션 방식으로 로그인을 처리하는 코드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증된 사용자라면 세션에 정보를 저장하고, 그렇지 않다면 검증 에러를 객체에 담아 적절한 뷰를 내려주는 식으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717830687768&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Controller
@RequiredArgsConstructor
public class LoginController {
    
    private final TokenProvider tokenProvider;

    ...

    @PostMapping(&quot;/login&quot;)
    public String login(@Valid @ModelAttribute(&quot;loginForm&quot;) LoginDto dto, BindingResult bindingResult,
                        @RequestParam(defaultValue = &quot;/&quot;) String redirectURL, HttpServletResponse res) {

        log.info(&quot;로그인 검증&quot;);

        if (bindingResult.hasErrors()) {
            log.info(&quot;검증 에러 errors={}&quot;, bindingResult);
            return &quot;login/signIn&quot;;
        }

        //로그인 유효성 검사
        Member loginMember = loginService.validationLogin(dto.getLoginId(), dto.getPassword());
        log.info(&quot;Login Member = {}&quot;, loginMember);

        if (loginMember == null) {
            bindingResult.reject(&quot;loginFail&quot;);
            return &quot;login/signIn&quot;;
        }

        //로그인 성공 처리
        log.info(&quot;정상 입력으로 로그인 성공&quot;);

        Map&amp;lt;String, Object&amp;gt; member = new HashMap&amp;lt;&amp;gt;();
        member.put(&quot;loginId&quot;, dto.loginId);
        member.put(&quot;password&quot;, dto.password);

        //10분 후 만료되는 jwt 생성하여 쿠키에 저장
        Cookie cookie = new Cookie(JwtLoginInterceptor.COOKIE_NAME, 
                             tokenProvider.createToken(member, Optional.of(LocalDateTime.now().plusMinutes(10)))
                        );

        cookie.setPath(&quot;/&quot;);
        cookie.setMaxAge(Integer.MAX_VALUE);
        
        res.addCookie(cookie);

        return &quot;redirect:&quot; + redirectURL; //로그인 후 최초 위치로 돌아가도록

    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 서비스에서 처리하거나 Spring Security 설정 클래스에서 해당 로직을 처리하는 커스텀 Filter를 등록하여 처리할 수도 있다. 어떤 방식을 사용하든 &lt;b&gt;Client의 요청마다 해당 로직이 동작&lt;/b&gt;한다. 이것은 &lt;u&gt;&lt;b&gt;추가적인 연산 오버헤드를 발생시켜 성능 저하를 초래할 수 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;  JWT의 보안 취약점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;JWT 방식은 다음과 같은 이유로 보안 취약점이 뚜렷하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; &lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;저장소가 Client 측에 있어 XSS(&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: left;&quot;&gt;Cross Site Scripting) 공격에 취약하다.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Payload가 Base64로 인코딩되었을 뿐, 암호화된 것이 아니다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;암호화 알고리즘에 취약점이 있을 수 있다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;발급한 JWT는 서버에서 통제할 방법이 없다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;  SSR 특성과의 부적합&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;JWT는 Client 측에서 관리하기 때문에 Javascript로 처리해야 한다.&lt;/b&gt; SSR 환경에서는 Javascript를 JQuery를 통해 HTML에 삽입하거나 Ajax를 통해 비동기 통신으로 처리해야 하므로 &lt;u&gt;&lt;b&gt;Javascript를 사용하기 불편하다&lt;/b&gt;&lt;/u&gt;는 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;References:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a href=&quot;https://zks145.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://zks145.tistory.com/106&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1717837055775&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring] SSR에서 JWT를 이용한 인증/인가 처리 고민&quot; data-og-description=&quot;SSR과 JWT에 대한 고찰여기저기 글을 찾아보면 LocalStorage, Cookie를 이용해 JWT 관리하게 되는데 Spring Boot에서 JSP, thymeleaf 같이 SSR 기반 방식의 경우 JWT를 사용하는 경우는 거의 없습니다. SSR 경우 대&quot; data-og-host=&quot;zks145.tistory.com&quot; data-og-source-url=&quot;https://zks145.tistory.com/106&quot; data-og-url=&quot;https://zks145.tistory.com/106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbdjR7/hyWleGg220/ddu9iPUtFR6sIobUWd1l51/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163,https://scrap.kakaocdn.net/dn/BX8up/hyWldUTTFN/J8K1vwt4NSpK5RYHKpTo2k/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163&quot;&gt;&lt;a href=&quot;https://zks145.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zks145.tistory.com/106&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbdjR7/hyWleGg220/ddu9iPUtFR6sIobUWd1l51/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163,https://scrap.kakaocdn.net/dn/BX8up/hyWldUTTFN/J8K1vwt4NSpK5RYHKpTo2k/img.png?width=310&amp;amp;height=163&amp;amp;face=0_0_310_163');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Spring] SSR에서 JWT를 이용한 인증/인가 처리 고민&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSR과 JWT에 대한 고찰여기저기 글을 찾아보면 LocalStorage, Cookie를 이용해 JWT 관리하게 되는데 Spring Boot에서 JSP, thymeleaf 같이 SSR 기반 방식의 경우 JWT를 사용하는 경우는 거의 없습니다. SSR 경우 대&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zks145.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;a href=&quot;https://okky.kr/questions/1488896&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://okky.kr/questions/1488896&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1717837026912&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;OKKY - Spring boot SSR에서는 JWT를 지양하는 이유?&quot; data-og-description=&quot;SSR 기반으로 클라이언트를 구성하던 도중에 JWT이용해 인증/인가 처리를 하려고 했습니다. 그런데 해당 방식으로 구현한 소스를 찾아보는데 SSR에서는 JWT를 이용하는 방식보다는 세션을 이용해 &quot; data-og-host=&quot;okky.kr&quot; data-og-source-url=&quot;https://okky.kr/questions/1488896&quot; data-og-url=&quot;https://okky.kr/questions/1488896&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ScyaS/hyWg7Pz0lW/PnXPuWzbcjcY83JfUwhk4K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://okky.kr/questions/1488896&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://okky.kr/questions/1488896&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ScyaS/hyWg7Pz0lW/PnXPuWzbcjcY83JfUwhk4K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;OKKY - Spring boot SSR에서는 JWT를 지양하는 이유?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSR 기반으로 클라이언트를 구성하던 도중에 JWT이용해 인증/인가 처리를 하려고 했습니다. 그런데 해당 방식으로 구현한 소스를 찾아보는데 SSR에서는 JWT를 이용하는 방식보다는 세션을 이용해&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;okky.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stove99.github.io/java/2019/08/02/springboot-with-jwt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stove99.github.io/java/2019/08/02/springboot-with-jwt/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1717837042746&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JAVA : SpringBoot2 &amp;amp; Thymeleaf 에서 JWT 이용해서 로그인 하기&quot; data-og-description=&quot;SpringBoot2 &amp;amp; Thymeleaf 로 구성된 전통적인(?) 환경에서 JWT 를 이용한 로그인 처리 샘플&quot; data-og-host=&quot;stove99.github.io&quot; data-og-source-url=&quot;https://stove99.github.io/java/2019/08/02/springboot-with-jwt/&quot; data-og-url=&quot;https://stove99.github.io//java/2019/08/02/springboot-with-jwt/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/srAKI/hyWldUTTES/dCxbdckcFkbxkUPWq3ujlK/img.png?width=216&amp;amp;height=217&amp;amp;face=0_0_216_217&quot;&gt;&lt;a href=&quot;https://stove99.github.io/java/2019/08/02/springboot-with-jwt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stove99.github.io/java/2019/08/02/springboot-with-jwt/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/srAKI/hyWldUTTES/dCxbdckcFkbxkUPWq3ujlK/img.png?width=216&amp;amp;height=217&amp;amp;face=0_0_216_217');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JAVA : SpringBoot2 &amp;amp; Thymeleaf 에서 JWT 이용해서 로그인 하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SpringBoot2 &amp;amp; Thymeleaf 로 구성된 전통적인(?) 환경에서 JWT 를 이용한 로그인 처리 샘플&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stove99.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Network</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/160</guid>
      <comments>https://dev-rootable.tistory.com/160#entry160comment</comments>
      <pubDate>Sat, 8 Jun 2024 17:57:52 +0900</pubDate>
    </item>
    <item>
      <title>허프만(Huffman) 알고리즘</title>
      <link>https://dev-rootable.tistory.com/159</link>
      <description>&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  허프만 알고리즘이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;압축 단위마다 문자의 출현 빈도를 조사하여 빈도가 높은 순서대로 비트 수가 적은 부호를 부여함으로써 데이터를 압축하는 방식&lt;/blockquote&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: #ffffdd;&quot;&gt;&lt;b&gt;많이 사용된 문자는 더 적은 비트로&lt;/b&gt;&lt;/span&gt; 나타내고, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;적게 사용된 문자는 더 많은 비트를&lt;/b&gt;&lt;/span&gt; 사용하여 효율적으로 문자열을 나타내는 방식을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  문자 압축 예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AAAAAABBBBCDD =&amp;gt; 'A' 6개, 'B' 4개, 'C' 1개, 'D' 2개&lt;/blockquote&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; &amp;zwj;♂️ 항 합치기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 적은 사용 빈도를 가진 두 항을 묶고 그 합을 적어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH1nBy/btsHGiqAcRY/w3YH3x8B2mCUxiENXMaiKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH1nBy/btsHGiqAcRY/w3YH3x8B2mCUxiENXMaiKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH1nBy/btsHGiqAcRY/w3YH3x8B2mCUxiENXMaiKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH1nBy%2FbtsHGiqAcRY%2Fw3YH3x8B2mCUxiENXMaiKK%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;618&quot; height=&quot;259&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;306&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBiJVR/btsHHaS0dfl/MrGYfyCpL9YwfsKFADRCvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBiJVR/btsHHaS0dfl/MrGYfyCpL9YwfsKFADRCvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBiJVR/btsHHaS0dfl/MrGYfyCpL9YwfsKFADRCvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBiJVR%2FbtsHHaS0dfl%2FMrGYfyCpL9YwfsKFADRCvk%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;589&quot; height=&quot;405&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;469&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;마지막 남은 A까지 묶어주면 다음과 같은 그림이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CR9vv/btsHEY0ZnTt/VHa6M69fffQrE0tLkiMeF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CR9vv/btsHEY0ZnTt/VHa6M69fffQrE0tLkiMeF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CR9vv/btsHEY0ZnTt/VHa6M69fffQrE0tLkiMeF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCR9vv%2FbtsHEY0ZnTt%2FVHa6M69fffQrE0tLkiMeF0%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;598&quot; height=&quot;534&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;602&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &amp;zwj;♂️ 비트 부여하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;루트 노드를 기준으로 왼쪽으로 한번 가면 0, 오른쪽으로 가면 1이라고 하겠다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A ➡ 0&lt;/li&gt;
&lt;li&gt;B ➡ 10&lt;/li&gt;
&lt;li&gt;C ➡ 110&lt;/li&gt;
&lt;li&gt;D ➡ 111&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 사용하여 &lt;b&gt;AAAAAABBBBCDD&lt;/b&gt;는 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt;00000010101010110111111&lt;/b&gt;이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이처럼 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;문자의 사용 빈도를 이용하여 문자열을 압축하는 알고리즘&lt;/b&gt;&lt;/span&gt;이 &lt;b&gt;허프만 알고리즘&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  특징&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 모호성 제거&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 다음과 같은 가변 길이 코드 변환 규칙이 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;{A, B, C, D} =&amp;gt; {0, 01, 10, 1}&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, AB와 AAD는 '001'로 동일하게 표현되기 때문에 모호성이 발생한다. 이를 해결하기 위해서는 &lt;b&gt;어떠한 문자도 다른 문자의 prefix를 가지지 않으면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 살펴봤던 허프만 코드는 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;{A, B, C, D} =&amp;gt; {0, 10, 110, 111}&lt;/b&gt;&lt;/blockquote&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;u&gt;&lt;b&gt;어떠한 문자도 다른 문자의 prefix가 되는 경우가 없다. 따라서&lt;/b&gt;&amp;nbsp;&lt;b&gt;허&lt;/b&gt;&lt;b&gt;프만 코드는 모호성이 없다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ 공간 절약&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 빈도의 문자열이 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;문자&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;빈도&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;fixed-length&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;variable-length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;A&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;60%&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;00&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;B&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;25%&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;01&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;C&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;10%&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;D&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;5%&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;11&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;111&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9b9b9b; color: #ffffff; text-align: start;&quot;&gt;fixed-length&lt;/span&gt; 코드를 사용하면 2비트만을 필요로 하기 때문에 &lt;b&gt;공간적인 측면에서 절약이 가능하지만 모호성&lt;/b&gt;이 발생할 수 있다. 반면, &lt;span style=&quot;background-color: #9b9b9b; color: #ffffff; text-align: start;&quot;&gt;variable-length&lt;/span&gt; 코드를 사용하면 1 ~ 3비트를 사용하므로 &lt;b&gt;공간적인 절약을 항상 보장할 수 없지만 prefix-free&lt;/b&gt; 코드이므로 모호성이 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;span style=&quot;background-color: #9b9b9b; color: #ffffff; text-align: start;&quot;&gt;variable-length&lt;/span&gt; 코드는 &lt;u&gt;&lt;b&gt;문자 출현 빈도에 따라 나타내는 비트 수가 다르기 때문에&lt;/b&gt;&lt;/u&gt; 평균적으로 1.55의 공간을 필요로 한다. 따라서 평균적으로는 &lt;span style=&quot;background-color: #9b9b9b; color: #ffffff; text-align: start;&quot;&gt;fixed-length&lt;/span&gt; 코드의 평균 2보다 &lt;u&gt;&lt;b&gt;더 나은 효율성&lt;/b&gt;&lt;/u&gt;을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  구현&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&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: #ffffdd;&quot;&gt;&lt;b&gt;허프만 알고리즘은 Heap을 통해 구현된다.&lt;/b&gt;&lt;/span&gt; 그래서 힙으로 구현된 우선순위 큐를 이용하여 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;먼저 문자의 빈도 수에 따른 이진 트리를 생성하기 위해 아래와 같이 Node 클래스를 작성한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716953669822&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node {
    char word;
    int freq;
    Node left = null, right = null;
    
    Node(char word, int freq) {
        this.word = word;
        this.freq = freq;
    }
    
    Node(Node left, Node right) {
        this.freq = left.freq + right.freq; //항 합치기
        this.left = left;
        this.right = right;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단어의 빈도를 &lt;b&gt;집계&lt;/b&gt;하는 &lt;b&gt;Map&lt;/b&gt;을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;각 Node의 빈도 수가 적은 것부터 추출하기 위해 &lt;b&gt;우선순위 큐&lt;/b&gt;를 이용하여 &lt;b&gt;이진 트리&lt;/b&gt;를 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;poll()을 수행할 때마다 빈도 수가 적은 것부터 추출되고, 이를 다시 합쳐서 큐에 넣는 작업을 반복한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;압축 결과를 출력하기 위해 optBits에 집계한 최적 비트를 통해 원본(txt)를 수정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716953923139&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void HuffmanCoding(String txt) {
    Map&amp;lt;Character, Integer&amp;gt; stat = new HashMap&amp;lt;&amp;gt;();
    for (char c : txt.toCharArray()) {
        stat.put(c, stat.getOrDefault(c, 0) + 1); //누적
    }
    
    //우선순위 큐를 통해 정렬
    PriorityQueue&amp;lt;Node&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;(Comparator.comparingInt(node -&amp;gt; node.freq));
    for (Map.Entry&amp;lt;Character, Integer&amp;gt; entry : stat.entrySet()) {
        pq.add(new Node(entry.getKey(), entry.getValue()));
    }
    
    //허프만 알고리즘에 따른 이진 트리 생성
    while (pq.size() &amp;gt; 1) {
        Node left = pq.poll();
        Node right = pq.poll();
        pq.add(new Node(left, right));
    }
    
    printCompression(pq.peek(), &quot;&quot;);
    
    for (Map.Entry&amp;lt;String, String&amp;gt; entry : optBits.entrySet()) {
        txt = txt.replaceAll(entry.getKey(), entry.getValue());
    }

    System.out.println(&quot;압축 결과: &quot; + txt);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이진 트리에서 왼쪽으로 이동할 때마다 '0'을 부여하고, 오른쪽으로 이동할 때마다 '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;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;노드의 좌우가 null이라는 것은 리프 노드에 도달했음을 의미하므로 출력을 수행한다. 이때, optBits 맵에 각 단어에 대한 &lt;span style=&quot;background-color: #9b9b9b; color: #ffffff; text-align: start;&quot;&gt;variable-length&lt;/span&gt; 코드를 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716954568403&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static void printCompression(Node node, String comp) {
    if (node == null) return;
    
    //리프 노드에 도달한 경우 출력
    if (node.left = null &amp;amp;&amp;amp; node.right == null) {
        optBits.put(node.word + &quot;&quot;, comp); //압축 결과를 출력하기 위해 Map에 저장
        System.out.println(node.word + &quot;-&amp;gt; &quot; + comp);
    }
    
    //비트 압축
    printCompression(node.left, comp + &quot;0&quot;);
    printCompression(node.right, comp + &quot;1&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716955859245&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Huffman {

    static Map&amp;lt;String, String&amp;gt; optBits = new HashMap&amp;lt;&amp;gt;();

    ...

    public static void main(String[] args) {
        Huffman o = new Huffman();
        o.HuffmanCoding(&quot;AAAAAABBBBCDD&quot;);
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zEGC3/btsHFMTz9Df/IveBzEJr138PRnytEogED1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zEGC3/btsHFMTz9Df/IveBzEJr138PRnytEogED1/img.png&quot; data-alt=&quot;&amp;quot;AAAAAABBBBCDD&amp;quot;에 대해 부여된 최적 이진 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zEGC3/btsHFMTz9Df/IveBzEJr138PRnytEogED1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzEGC3%2FbtsHFMTz9Df%2FIveBzEJr138PRnytEogED1%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;410&quot; height=&quot;154&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;AAAAAABBBBCDD&quot;에 대해 부여된 최적 이진 코드&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;color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;이 결과는 아래와 같은 이진 트리가 만들어진 케이스다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcin5b/btsHG9NumpG/bczkV9hfAlZPPhAYyEG2I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcin5b/btsHG9NumpG/bczkV9hfAlZPPhAYyEG2I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcin5b/btsHG9NumpG/bczkV9hfAlZPPhAYyEG2I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcin5b%2FbtsHG9NumpG%2FbczkV9hfAlZPPhAYyEG2I0%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;508&quot; height=&quot;520&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그래서 최적 이진 코드는 하나의 케이스만 나오지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;References:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;a href=&quot;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716953533123&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;백준 13975번 : 파일 합치기 3 java&quot; data-og-description=&quot;이 문제는 허프만 알고리즘을 이용하여 푸는 문제입니다. 허프만 알고리즘에 대하여 우선 설명드리도록 하겠습니다. 허프만 알고리즘이란 압축 단위마다 문자의 출현 빈도를 조사하여 빈도가 &quot; data-og-host=&quot;dy-coding.tistory.com&quot; data-og-source-url=&quot;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&quot; data-og-url=&quot;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YGqpO/hyV9RNrct8/6kTinImCHcBD8EXJjP1z3K/img.png?width=800&amp;amp;height=415&amp;amp;face=0_0_800_415,https://scrap.kakaocdn.net/dn/bOVnBr/hyV9VWDacn/BmsV3jeJ60uFfHacf7qblK/img.png?width=800&amp;amp;height=415&amp;amp;face=0_0_800_415,https://scrap.kakaocdn.net/dn/nhllk/hyV904GYrm/OUK9KK2pPC77S1oOPNssr1/img.png?width=1464&amp;amp;height=761&amp;amp;face=0_0_1464_761&quot;&gt;&lt;a href=&quot;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dy-coding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13975%EB%B2%88-%ED%8C%8C%EC%9D%BC-%ED%95%A9%EC%B9%98%EA%B8%B0-3-java&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YGqpO/hyV9RNrct8/6kTinImCHcBD8EXJjP1z3K/img.png?width=800&amp;amp;height=415&amp;amp;face=0_0_800_415,https://scrap.kakaocdn.net/dn/bOVnBr/hyV9VWDacn/BmsV3jeJ60uFfHacf7qblK/img.png?width=800&amp;amp;height=415&amp;amp;face=0_0_800_415,https://scrap.kakaocdn.net/dn/nhllk/hyV904GYrm/OUK9KK2pPC77S1oOPNssr1/img.png?width=1464&amp;amp;height=761&amp;amp;face=0_0_1464_761');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;백준 13975번 : 파일 합치기 3 java&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 허프만 알고리즘을 이용하여 푸는 문제입니다. 허프만 알고리즘에 대하여 우선 설명드리도록 하겠습니다. 허프만 알고리즘이란 압축 단위마다 문자의 출현 빈도를 조사하여 빈도가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dy-coding.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/2478/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yozm.wishket.com/magazine/detail/2478/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716957421943&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;탐욕 알고리즘과 허프만 코딩 구현 방법 | 요즘IT&quot; data-og-description=&quot;탐욕 알고리즘(Greedy Algorithm)은 각 단계에서 최적의 해결책을 선택하여 복잡한 문제를 간단하고 빠르게 해결하는 알고리즘을 말합니다. 이번 글에서는 탐욕 알고리즘의 기본 개념과 작동 원리를&quot; data-og-host=&quot;yozm.wishket.com&quot; data-og-source-url=&quot;https://yozm.wishket.com/magazine/detail/2478/&quot; data-og-url=&quot;https://yozm.wishket.com/magazine/detail/2478/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OHiU6/hyWdsFf9GQ/szISyqkFFJkqjUkG1ERK10/img.png?width=672&amp;amp;height=461&amp;amp;face=0_0_672_461,https://scrap.kakaocdn.net/dn/cXg9Ko/hyWdgrh65k/DOzFJsJVr8MzJC28az61tK/img.png?width=672&amp;amp;height=461&amp;amp;face=0_0_672_461&quot;&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/2478/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yozm.wishket.com/magazine/detail/2478/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OHiU6/hyWdsFf9GQ/szISyqkFFJkqjUkG1ERK10/img.png?width=672&amp;amp;height=461&amp;amp;face=0_0_672_461,https://scrap.kakaocdn.net/dn/cXg9Ko/hyWdgrh65k/DOzFJsJVr8MzJC28az61tK/img.png?width=672&amp;amp;height=461&amp;amp;face=0_0_672_461');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;탐욕 알고리즘과 허프만 코딩 구현 방법 | 요즘IT&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;탐욕 알고리즘(Greedy Algorithm)은 각 단계에서 최적의 해결책을 선택하여 복잡한 문제를 간단하고 빠르게 해결하는 알고리즘을 말합니다. 이번 글에서는 탐욕 알고리즘의 기본 개념과 작동 원리를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yozm.wishket.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716963704600&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[알고리즘] Huffman code / 허프만코드&quot; data-og-description=&quot; Huffman code 란?압축은 자료의 크기를 줄이기 위해서 사용된다. 그리고 이 압축을 하는 방식에 따라서 압축된 파일의 용량이 달라질 수 있는데, 그 중 Huffman code는 문자의 출현 빈도에 따라서 다&quot; data-og-host=&quot;mirrorofcode.tistory.com&quot; data-og-source-url=&quot;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&quot; data-og-url=&quot;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mHIeW/hyWddOTlrE/TGMz9xGChui7vApDoed8mk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431,https://scrap.kakaocdn.net/dn/cy3a73/hyWdrGnAcI/2pzIgSK2qOUGb0w8totiBk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431,https://scrap.kakaocdn.net/dn/bcYPnQ/hyWdrTTIUQ/LRUGXuRoNj9D67ck1tALkk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431&quot;&gt;&lt;a href=&quot;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mirrorofcode.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Huffman-code-%ED%97%88%ED%94%84%EB%A7%8C%EC%BD%94%EB%93%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mHIeW/hyWddOTlrE/TGMz9xGChui7vApDoed8mk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431,https://scrap.kakaocdn.net/dn/cy3a73/hyWdrGnAcI/2pzIgSK2qOUGb0w8totiBk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431,https://scrap.kakaocdn.net/dn/bcYPnQ/hyWdrTTIUQ/LRUGXuRoNj9D67ck1tALkk/img.jpg?width=644&amp;amp;height=431&amp;amp;face=0_0_644_431');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[알고리즘] Huffman code / 허프만코드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt; Huffman code 란?압축은 자료의 크기를 줄이기 위해서 사용된다. 그리고 이 압축을 하는 방식에 따라서 압축된 파일의 용량이 달라질 수 있는데, 그 중 Huffman code는 문자의 출현 빈도에 따라서 다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mirrorofcode.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/159</guid>
      <comments>https://dev-rootable.tistory.com/159#entry159comment</comments>
      <pubDate>Wed, 29 May 2024 13:37:18 +0900</pubDate>
    </item>
    <item>
      <title>람다식과 함수형 인터페이스</title>
      <link>https://dev-rootable.tistory.com/158</link>
      <description>&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❓ 람다식이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;메서드를 하나의 식(expression)으로 표현한 것&lt;/blockquote&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;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;익명 함수(anonymous function)&lt;/b&gt;&lt;/span&gt;'라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349217012&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int method() {
    return (int) (Math.random() * 5) + 1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 메서드를 람다식으로 바꾸면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349145953&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] arr = new int[5];
Arrays.setAll(arr, (i) -&amp;gt; (int) (Math.random() * 5) + 1);&lt;/code&gt;&lt;/pre&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 메서드는 클래스에 포함되어야 하므로 클래스를 새로 만들어야 하고, 객체도 생성해야만 비로소 메서드를 호출할 수 있다. 그러나 람다식은 이러한 과정 없이 오직 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식 자체만으로도 메서드의 역할을 대신할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식은 메서드의 매개변수로 전달이 가능&lt;/b&gt;&lt;/span&gt;하고, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식 자체를 메서드의 결괏값으로 반환할 수도 있다.&lt;/b&gt;&lt;/span&gt; 이처럼 람다식으로 인해 메서드를 변수처럼 다루는 것이 가능해진 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;함수형 인터페이스와 결합하여 코드의 간결성과 가독성을 향상할&lt;/b&gt;&lt;/span&gt; 수 있다. 자바에서는 기본적으로 사용할 수 있는 다양한 함수형 인터페이스를 제공한다. 대표적으로 Runnable, Callable, Comparator, Predicate, Function, Consumer, Supplier 등이 있다. 이러한 함수형 인터페이스를 통해 대부분의 코드를 처리할 수 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;자바 람다식과 함수형 인터페이스를 사용하면 익명 클래스의 선언을 대체할 수 있다.&lt;/b&gt;&lt;/span&gt; 이를 통해 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;코드를 더욱 간결하게 만들고, 익명 클래스의 중복 사용에 따른 코드의 중복을 줄일 수 있다.&lt;/b&gt;&lt;/span&gt; 예를 들어 여러 번 정렬이 필요한 상황에서 Comparator를 사용한다면 코드 중복이 발생하고 코드 가독성이 떨어지게 되는데, 이러한 상황에서 람다식을 사용하여 해결할 수 있다.&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;✔ 스트림 API 활용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 8부터 도입된 스트림 API는 컬렉션에 저장된 데이터를 간결하게 처리할 수 있는 기능을 제공한다. 특히 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식과 스트림 API를 결합하여 데이터 필터링, 매핑, 집계 등의 작업을 직관적으로 수행할 수 있다.&lt;/b&gt;&lt;/span&gt; 이러한 스트림 API를 활용하여 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;함수형 프로그래밍 스타일로 더욱 직관적이고 간결한 코드&lt;/b&gt;&lt;/span&gt;를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  람다식 작성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;람다식은 메서드의 매개변수 타입을 생략할 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;단, 두 매개변수 중 어느 하나의 타입만 생략하는 것은 허용되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반환값이 있는 메서드의 경우, return문 대신 식(expression)으로 대신할 수 있다. 식의 연산 결과가 자동으로 반환값이 된다. (세미콜론 제외)&lt;/li&gt;
&lt;li&gt;매개변수가 하나인 경우에는 괄호를 생략할 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;단, 매개변수의 타입이 있으면 괄호를 생략할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;{} 안의 명령문이 하나일 때는 {}를 생략할 수 있고, 세미콜론을 붙이지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349663839&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int max(int a, int b) {
    return a &amp;gt; b ? a : b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349678830&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(a, b) -&amp;gt; a &amp;gt; b ? a : b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  함수형 인터페이스(Functional Interface)와 람다식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;람다식은 익명 클래스의 객체와 동등하다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349892434&quot; class=&quot;livecodeserver&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;(a, b) -&amp;gt; a &amp;gt; b ? a : b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716349935474&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;new Object() {
    int max(int a, int b) {
        return a &amp;gt; b ? a : b;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 람다식이고, 아래는 이것을 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;익명 클래스의 객체&lt;/b&gt;&lt;/span&gt;로 변환한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;b&gt;메서드를 호출하려면 익명 클래스의 객체를 받아야 하는데&lt;/b&gt;, &lt;b&gt;참조형이므로 클래스 또는 인터페이스&lt;/b&gt;로 받아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716351236529&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;타입 f = (a, b) -&amp;gt; a &amp;gt; b ? a : b;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;람다식을 받을 수 있는 클래스 또는 인터페이스는 람다식과 동등한 메서드가 정의되어 있는 것이어야 한다.&lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716351428771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface MyFunction {
    public abstract int max(int a, int b);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인터페이스를 구현한 익명 클래스의 객체를 다음과 같이 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716351538487&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyFunction f = new MyFunction() {
                   public int max(int a, int b) {
                       return a &amp;gt; b ? a : b;
                   }
               };
               
int maxVal = f.max(5, 3); //익명 객체의 메서드 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;MyFunction 인터페이스에 정의된 메서드 max()가 람다식과 일치하기 때문에 아래와 같이 대체할 수 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716351655632&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyFunction f = (a, b) -&amp;gt; a &amp;gt; b ? a : b; //익명 객체를 람다식으로 대체
int maxVal = f.max(5, 3); //익명 객체의 메서드 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 익명 객체를 람다식으로 교체 가능한 이유는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식도 실제로는 익명 객체이기 때문&lt;/b&gt;&lt;/span&gt;이다. 이렇게 대체하기 위해서는 &lt;u&gt;&lt;b&gt;람다식의 매개변수 타입 및 개수와 반환값이 일치해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  결론&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;지금까지 살펴본 것처럼, 하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바 규칙들을 어기지 않으면서도 자연스럽다.&lt;br /&gt;&lt;br /&gt;그래서 &lt;b&gt;인터페이스를 통해 람다식을 다루기로 결정&lt;/b&gt;되었으며, &lt;b&gt;람다식을 다루기 위한 인터페이스를 '&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;함수형 인터페이스(functional interface)&lt;/span&gt;'라고 부르기로 했다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;
&lt;pre id=&quot;code_1716352019359&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface //컴파일러가 함수형 인터페이스를 올바르게 정의했는지 확인함
interface MyFunction { //함수형 인터페이스
    public abstract int max(int a, int b);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
단, &lt;b&gt;&lt;/b&gt;&lt;u&gt;&lt;b&gt;함수형 인터페이스는 오직 하나의 추상 메서드만 정의되어 있어야 한다는 제약&lt;/b&gt;&lt;/u&gt;이 있다. 그래야 람다식과 인터페이스의 메서드가 일대일로 연결되기 때문이다. static, default 메서드의 개수는 제약이 없다.&lt;/blockquote&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;메서드의 매개변수가 MyFunction(함수형 인터페이스) 타입이면, MyFunction의 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야 한다.&lt;/b&gt;&lt;/u&gt; 마찬가지로 람다식은 함수형 인터페이스의 추상 메서드와 동등해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716352604578&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
interface MyFunction {
    void myMethod();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716352687723&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void aMethod (MyFunction f) { //매개변수 타입이 함수형 인터페이스
    f.myMethod(); //함수형 인터페이스의 추상 메서드 호출
}

MyFunction f = () -&amp;gt; System.out.println(&quot;myMethod()&quot;); //람다식을 참조하는 참조변수
aMethod(f);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조변수 없이 아래와 같이 직접 람다식을 매개변수로 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716353316904&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aMethod(() -&amp;gt; System.out.println(&quot;myMethod()&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드의 반환타입이 함수형 인터페이스라면, 해당 함수형 인터페이스의 추상 메서드와 동등한 람다식을 가리키는 참조변수를 반환하거나 람다식을 직접 반환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716353461161&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyFunction myMethod() {
    MyFunction f = () -&amp;gt; {};
    return f; //return () -&amp;gt; {}; 으로 줄일 수 있음
}&lt;/code&gt;&lt;/pre&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;람다식은 오직 함수형 인터페이스로만 형변환이 가능&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716354016063&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
interface MyFunction {
    void method();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수형 인터페이스의 메서드와 동등한 람다식(매개변수 &amp;amp; 반환값 일치)을 함수형 인터페이스로 형변환할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716354062422&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyFunction f = (MyFunction) (() -&amp;gt; System.out.println(&quot;Lambda&quot;));
MyFunction f = (Object) (() -&amp;gt; System.out.println(&quot;Lambda&quot;)); //불가능
Object o = (Object) (MyFunction) (() -&amp;gt; System.out.println(&quot;Lambda&quot;)); //가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;람다식은 익명 객체지만 타입이 존재한다.&lt;/b&gt;&lt;b&gt; 하지만 컴파일러가 임의로 이름을 정하기 때문에 알 수 없는 것이다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MOGlX/btsHw5TAGDu/KGGLhbOn9cK8mKzhjWH8X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MOGlX/btsHw5TAGDu/KGGLhbOn9cK8mKzhjWH8X0/img.png&quot; data-alt=&quot;람다식의 타입: 외부클래스이름$$Lambda$번호&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MOGlX/btsHw5TAGDu/KGGLhbOn9cK8mKzhjWH8X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMOGlX%2FbtsHw5TAGDu%2FKGGLhbOn9cK8mKzhjWH8X0%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;460&quot; height=&quot;107&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;람다식의 타입: 외부클래스이름$$Lambda$번호&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;b&gt;일반적인 익명 객체&lt;/b&gt;는 객체의 타입이 '&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;외부클래스이름$번호&lt;/b&gt;&lt;/span&gt;'와 같은 형식으로 타입이 결정되지만 &lt;b&gt;람다식의 타입&lt;/b&gt;은 '&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;외부클래스이름$$Lambda$번호&lt;/b&gt;&lt;/span&gt;'와 같은 형식으로 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  람다식에서 외부 변수 참조&lt;/b&gt;&lt;/h4&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;람다식에서 참조하는 지역 변수는 final이 붙지 않았어도 상수로 간주한다.&lt;/b&gt;&lt;/span&gt; 따라서, &lt;u&gt;&lt;b&gt;람다식 내에서 참조하는 지역 변수는 람다식 내에서나 다른 어느 곳에서나 값을 변경할 수 없다.&lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;람다식 밖 클래스의 인스턴스 변수는 람다식 내에서 참조하더라도 상수로 간주되지 않으므로 값을 변경할 수 있다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716355149036&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
interface MyFunction2 {
    void myMethod();
}

class Outer{
    int val = 10; //Outer.this.val
	
    class Inner{
        int val = 20; //this.val
		
        void method(int i) { //void method(final int i)
            int val = 30; //final int val = 30;
//          i = 10; error -&amp;gt; final val can't change
			
            MyFunction2 f = () -&amp;gt; {
                System.out.println(&quot;             i : &quot; + i);
                System.out.println(&quot;           val : &quot; + val);
                System.out.println(&quot;      this.val : &quot; + this.val);
                System.out.println(&quot;Outer.this.val : &quot; + Outer.this.val);
            };
			
            f.myMethod();
        }
    }
}

public class LambdaEx3 {
	public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.method(100);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddQAB3/btsHw4ArLnh/NUQjM7KbNncaxtF8j0Q3Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddQAB3/btsHw4ArLnh/NUQjM7KbNncaxtF8j0Q3Y1/img.png&quot; data-alt=&quot;Test 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddQAB3/btsHw4ArLnh/NUQjM7KbNncaxtF8j0Q3Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddQAB3%2FbtsHw4ArLnh%2FNUQjM7KbNncaxtF8j0Q3Y1%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;206&quot; height=&quot;88&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Test 결과&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;u&gt;&lt;b&gt;외부 지역 변수와 동일 이름의 람다식 매개변수는 허용되지 않는다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1716363691076&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
interface MyFunction2 {
    void myMethod();
}

class Outer{
    int val = 10; //Outer.this.val
	
    class Inner{
        int val = 20; //this.val
		
        void method(int i) { //void method(final int i)
            int val = 30; //final int val = 30;
//          i = 10; error -&amp;gt; final val can't change
			
            MyFunction2 f = (i) -&amp;gt; { //error -&amp;gt; i는 변수 이름 중복
                System.out.println(&quot;             i : &quot; + i);
                System.out.println(&quot;           val : &quot; + val);
                System.out.println(&quot;      this.val : &quot; + this.val);
                System.out.println(&quot;Outer.this.val : &quot; + Outer.this.val);
            };
			
            f.myMethod();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❓ 익명 객체(익명 클래스)란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;프로그램에서 일시적으로 한 번만 사용되고 버려지는 객체&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  익명 객체(익명 클래스)를 사용하는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;익명 객체는 나중에 재사용이 되지 않는다.&lt;/b&gt;&lt;/span&gt; 이 말은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;확장성이 그렇게 좋지 못하다&lt;/b&gt;&lt;/span&gt;는 것을 의미한다. 자바에서 &lt;u&gt;&lt;b&gt;확장성을 고려한 설계는 매우 중요하지만 이로 인해 유지보수에서 더 불리한 경우도&lt;/b&gt;&lt;/u&gt; 있다. 그래서 다음과 같은 경우에 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로그램 내에서 일회용으로 사용되어야 하는 객체 (ex. UI 이벤트 처리, 스레드 객체 등 단발성 이벤트 처리)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재사용성이 없고, 확장성을 활용하는 것이 유지보수에서 더 불리할 때&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;비즈니스 로직이 재각각이며, 재사용성이 전혀 없어 매번 클래스를 생성해야 하는 비용이 큰 상황&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;References:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://limkydev.tistory.com/226&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://limkydev.tistory.com/226&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716366298430&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Java] 익명객체(익명클래스)란? (이 글 하나로 한방에 정리!)&quot; data-og-description=&quot;익명객체(익명클래스) 란? 이번시간에는 자바 익명객체(익명클래스)에 대해서 알아보도록 하겠습니다. 익명객체(익명클래스) 말그대로.. 이름이 없는 객체? 클래스?,,,그래서 무명클래스라고도 &quot; data-og-host=&quot;limkydev.tistory.com&quot; data-og-source-url=&quot;https://limkydev.tistory.com/226&quot; data-og-url=&quot;https://limkydev.tistory.com/226&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brwmz0/hyV58V1fWv/dtp5fnBW9xt4RTqolXqNfk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=0_0_300_168,https://scrap.kakaocdn.net/dn/cHx35C/hyV9OVUoUm/AFhLyqOzgKpkazCoEri6K0/img.jpg?width=300&amp;amp;height=168&amp;amp;face=0_0_300_168,https://scrap.kakaocdn.net/dn/4wjtJ/hyV6hrSGlw/fIzv2FK2mrDWyZsar7K9bk/img.png?width=767&amp;amp;height=967&amp;amp;face=0_0_767_967&quot;&gt;&lt;a href=&quot;https://limkydev.tistory.com/226&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://limkydev.tistory.com/226&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brwmz0/hyV58V1fWv/dtp5fnBW9xt4RTqolXqNfk/img.jpg?width=300&amp;amp;height=168&amp;amp;face=0_0_300_168,https://scrap.kakaocdn.net/dn/cHx35C/hyV9OVUoUm/AFhLyqOzgKpkazCoEri6K0/img.jpg?width=300&amp;amp;height=168&amp;amp;face=0_0_300_168,https://scrap.kakaocdn.net/dn/4wjtJ/hyV6hrSGlw/fIzv2FK2mrDWyZsar7K9bk/img.png?width=767&amp;amp;height=967&amp;amp;face=0_0_767_967');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 익명객체(익명클래스)란? (이 글 하나로 한방에 정리!)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;익명객체(익명클래스) 란? 이번시간에는 자바 익명객체(익명클래스)에 대해서 알아보도록 하겠습니다. 익명객체(익명클래스) 말그대로.. 이름이 없는 객체? 클래스?,,,그래서 무명클래스라고도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;limkydev.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/2023/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yozm.wishket.com/magazine/detail/2023/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1716366287473&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코드 가독성 높이는 자바 람다식과 함수형 인터페이스 | 요즘IT&quot; data-og-description=&quot;자바(Java)는 시간이 지남에 따라 발전하면서 더욱 효율적이고 간결한 코드를 작성할 수 있도록 다양한 기능을 제공하고 있습니다. 특히 자바 8부터 도입된 람다식과 함수형 인터페이스는 자바에&quot; data-og-host=&quot;yozm.wishket.com&quot; data-og-source-url=&quot;https://yozm.wishket.com/magazine/detail/2023/&quot; data-og-url=&quot;https://yozm.wishket.com/magazine/detail/2023/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZ7MA1/hyV6gT0vND/0pk8FmRbKiIIqEYMoUc3i1/img.png?width=1504&amp;amp;height=838&amp;amp;face=0_0_1504_838,https://scrap.kakaocdn.net/dn/sXfX4/hyV9XrLMO8/NkK3PCGN2YcWm8B75nwCQ1/img.png?width=1504&amp;amp;height=838&amp;amp;face=0_0_1504_838&quot;&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/2023/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yozm.wishket.com/magazine/detail/2023/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZ7MA1/hyV6gT0vND/0pk8FmRbKiIIqEYMoUc3i1/img.png?width=1504&amp;amp;height=838&amp;amp;face=0_0_1504_838,https://scrap.kakaocdn.net/dn/sXfX4/hyV9XrLMO8/NkK3PCGN2YcWm8B75nwCQ1/img.png?width=1504&amp;amp;height=838&amp;amp;face=0_0_1504_838');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코드 가독성 높이는 자바 람다식과 함수형 인터페이스 | 요즘IT&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바(Java)는 시간이 지남에 따라 발전하면서 더욱 효율적이고 간결한 코드를 작성할 수 있도록 다양한 기능을 제공하고 있습니다. 특히 자바 8부터 도입된 람다식과 함수형 인터페이스는 자바에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yozm.wishket.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDvXDJ/btsHyEmKfih/Gk5lG6fVT5D9hfgk13R9L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDvXDJ/btsHyEmKfih/Gk5lG6fVT5D9hfgk13R9L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDvXDJ/btsHyEmKfih/Gk5lG6fVT5D9hfgk13R9L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDvXDJ%2FbtsHyEmKfih%2FGk5lG6fVT5D9hfgk13R9L1%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;373&quot; height=&quot;518&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Java</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/158</guid>
      <comments>https://dev-rootable.tistory.com/158#entry158comment</comments>
      <pubDate>Wed, 22 May 2024 14:25:45 +0900</pubDate>
    </item>
    <item>
      <title>Java의 static 키워드</title>
      <link>https://dev-rootable.tistory.com/157</link>
      <description>&lt;h3 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❓ static 이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;모든 인스턴스에서 공유되는 static 변수&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 static 키워드는 클래스 레벨의 변수나 메서드, 블록을 정의할 때 사용된다. 이는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;인스턴스 생성 없이도 접근 가능하며, 모든 인스턴스에서 공유된다.&lt;/b&gt;&lt;/span&gt; static 변수는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;클래스가 로드될 때 Method 영역에 할당되기 때문에 프로그램 시작부터 종료까지 메모리에 유지된다.&lt;/b&gt;&lt;/span&gt; 이러한 특성으로 인해 &lt;u&gt;&lt;b&gt;모든 인스턴스가 공유하여 사용할 수 있는 변수&lt;/b&gt;&lt;/u&gt;가 필요할 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  static 변수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static 변수는 &lt;b&gt;클래스 레벨에서 선언&lt;/b&gt;되며, &lt;b&gt;모든 인스턴스에 의해 공유된다.&lt;/b&gt; 그래서 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;특정 데이터를 모든 인스턴스가 공유해야 할 때 유용&lt;/b&gt;&lt;/span&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 static 변수 사용 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715914174358&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Calculator {

    public static int value; //default 0
    
    public void add() {
        value++;
    }
    
    public void subtract() {
        value--;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  static 메서드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;정적인 값(상태를 가지지 않는 값)에 대한 연산에 유용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static 메서드는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;인스턴스 생성 없이 클래스 이름을 통해 직접 호출&lt;/b&gt;&lt;/span&gt;할 수 있다. 주로 유틸리티 함수나 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;인스턴스의 상태에 의존하지 않는 연산&lt;/b&gt;&lt;/span&gt;을 수행할 때 유용하다. 왜냐하면 &lt;u&gt;&lt;b&gt;static 메서드는 클래스의 다른 static 메서드나 변수만 접근할 수 있고, 인스턴스 멤버에 접근할 수 없기 때문이다.&lt;/b&gt;&lt;/u&gt; 즉, &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;정적인 값&lt;/b&gt;&lt;/span&gt;만 다룬다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 Java의 인스턴스 멤버를 살펴보자.&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;인스턴스 멤버는 new 연산자를 통해 객체를 생성한 후 사용할 수 있는 값으로 객체 없이는 사용할 수 없다. 인스턴스 필드는 Heap 영역에 객체마다 따로 존재하고, 인스턴스 메서드는 Method 영역에 저장되어 같은 클래스의 인스턴스끼리 공유한다. 여기서 알 수 있는 점은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;인스턴스 멤버는 객체 단위로 메모리를 할당받고, 그 객체의 변수나 메서드를 공유&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;객체에 묶여 있는 인스턴스 멤버는 상태가 있는 데이터를 다루고, 클래스에 묶여 있는 static 메서드는 상태가 없는 데이터를 다룬다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715915482445&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyCalculator {

    public static int add(int x, int y) {
        return x + y;
    }
    
    public int subtract(int x, int y) {
        return x - y;
    }
    
}

    MyCalculator.add(3, 4); //static method =&amp;gt; 클래스명으로 사용 o
    MyCalculator.subtract(5, 3); //not static method =&amp;gt; 객체 생성 후 사용해야 함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  static 블록&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static 블록은 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;클래스가 처음 로드될 때 한 번만 실행되는 코드 블록&lt;/b&gt;&lt;/span&gt;이다. 그래서 프로그램 시작 시 필요한 리소스를 준비하는 &lt;span style=&quot;background-color: #ffffdd;&quot;&gt;&lt;b&gt;초기화 작업&lt;/b&gt;&lt;/span&gt;에 적합하다. 주로 static 변수의 초기화에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715916197440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static class Machine {

    static String type = &quot;Init&quot;;
    
    static {
        System.out.println(&quot;Operating Test&quot;);
        type = &quot;Testing&quot;;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  static 키워드의 남용 주의&lt;/b&gt;&lt;/h3&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 static 키워드는 동일한 값을 유지하기 때문에 인스턴스처럼 복사본을 유지할 필요가 없으므로 &lt;b&gt;메모리 사용을 최적화할 수 있고 속도가 빠르다.&lt;/b&gt; 하지만 static 변수가 남용되면 오히려 프로그램 퍼포먼스에 부정적인 영향을 줄 수 있다.&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;static 변수가 저장되는 곳은 Method 영역이다. 해당 영역은 명시적 NULL을 선언하지 않는 한 프로그램 종료까지 메모리에 생존한다. 그래서 &lt;u&gt;&lt;b&gt;프로그램 생명주기 동안 메모리에 상주하므로, 불필요한 static 사용은 프로그램 성능 저하로 이어질 수 있다.&lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static은 객체지향적이지 못하다.&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;u&gt;&lt;b&gt;static은 따로 객체를 생성하지 않고 클래스를 통해 직접 데이터를 불러오기 때문에 각 객체의 데이터들이 캡슐화되어야 한다는 객체지향 프로그래밍의 원칙을 위반한다.&lt;/b&gt;&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;static 메서드는 Interface를 구현하는 데 사용될 수 없다.&lt;/b&gt;&lt;/u&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;b&gt;References:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=1&amp;amp;gclid=Cj0KCQjw3ZayBhDRARIsAPWzx8olhQnAc3pmvw4u5YKdOQbAZLied7ddvvXDfql8eWX-SwyPozlANxcaAuTtEALw_wcB&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=1&amp;amp;gclid=Cj0KCQjw3ZayBhDRARIsAPWzx8olhQnAc3pmvw4u5YKdOQbAZLied7ddvvXDfql8eWX-SwyPozlANxcaAuTtEALw_wcB&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715917405480&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;자바에서의 static 키워드 이해와 올바른 사용법&quot; data-og-description=&quot;자바에서 static 키워드의 기본적인 사용법과 주의점, 그리고 효과적인 사용 사례에 대해 알아보는 글입니다.&quot; data-og-host=&quot;f-lab.kr&quot; data-og-source-url=&quot;https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=1&amp;amp;gclid=Cj0KCQjw3ZayBhDRARIsAPWzx8olhQnAc3pmvw4u5YKdOQbAZLied7ddvvXDfql8eWX-SwyPozlANxcaAuTtEALw_wcB&quot; data-og-url=&quot;https://f-lab.kr/ai-blog/understanding-and-using-java-static-keyword&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sdS2D/hyV55Ya9xj/V3I1MaD60KZp0A7ovPdXvk/img.jpg?width=1792&amp;amp;height=1024&amp;amp;face=0_0_1792_1024,https://scrap.kakaocdn.net/dn/dyeIvC/hyV6e1Ohhy/N1Sgx1HMuamJJkU921F3X1/img.jpg?width=1792&amp;amp;height=1024&amp;amp;face=0_0_1792_1024&quot;&gt;&lt;a href=&quot;https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=1&amp;amp;gclid=Cj0KCQjw3ZayBhDRARIsAPWzx8olhQnAc3pmvw4u5YKdOQbAZLied7ddvvXDfql8eWX-SwyPozlANxcaAuTtEALw_wcB&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://f-lab.kr/insight/understanding-and-using-java-static-keyword?gad_source=1&amp;amp;gclid=Cj0KCQjw3ZayBhDRARIsAPWzx8olhQnAc3pmvw4u5YKdOQbAZLied7ddvvXDfql8eWX-SwyPozlANxcaAuTtEALw_wcB&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sdS2D/hyV55Ya9xj/V3I1MaD60KZp0A7ovPdXvk/img.jpg?width=1792&amp;amp;height=1024&amp;amp;face=0_0_1792_1024,https://scrap.kakaocdn.net/dn/dyeIvC/hyV6e1Ohhy/N1Sgx1HMuamJJkU921F3X1/img.jpg?width=1792&amp;amp;height=1024&amp;amp;face=0_0_1792_1024');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;자바에서의 static 키워드 이해와 올바른 사용법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바에서 static 키워드의 기본적인 사용법과 주의점, 그리고 효과적인 사용 사례에 대해 알아보는 글입니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;f-lab.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@gudonghee2000/static-vs-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@gudonghee2000/static-vs-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715917520321&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Static사용의 장점과 단점&quot; data-og-description=&quot;* Static * Static : 자바 언어 예약어로 클래스 메서드와 클래스 필드를 지정할 때 사용한다. 인스턴스를 생성하지 않아도 클래스의 메서드와 필드에 접근을 가능하게 한다. * Static 함수, 변수의 메&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@gudonghee2000/static-vs-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4&quot; data-og-url=&quot;https://velog.io/@gudonghee2000/static-vs-싱글톤-패턴&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/burJbt/hyV6aLSNaL/AYRxHk8dNVpZKvBzlKfnk1/img.png?width=951&amp;amp;height=406&amp;amp;face=0_0_951_406,https://scrap.kakaocdn.net/dn/h0LZW/hyV6gyzzMJ/zfS3rWFmMkss6OM40FFpr1/img.png?width=951&amp;amp;height=406&amp;amp;face=0_0_951_406,https://scrap.kakaocdn.net/dn/hNTmT/hyV58AwUnd/SOGuZLRq8uFnMmTdYSVSrK/img.png?width=427&amp;amp;height=427&amp;amp;face=140_104_254_228&quot;&gt;&lt;a href=&quot;https://velog.io/@gudonghee2000/static-vs-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@gudonghee2000/static-vs-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/burJbt/hyV6aLSNaL/AYRxHk8dNVpZKvBzlKfnk1/img.png?width=951&amp;amp;height=406&amp;amp;face=0_0_951_406,https://scrap.kakaocdn.net/dn/h0LZW/hyV6gyzzMJ/zfS3rWFmMkss6OM40FFpr1/img.png?width=951&amp;amp;height=406&amp;amp;face=0_0_951_406,https://scrap.kakaocdn.net/dn/hNTmT/hyV58AwUnd/SOGuZLRq8uFnMmTdYSVSrK/img.png?width=427&amp;amp;height=427&amp;amp;face=140_104_254_228');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Static사용의 장점과 단점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;* Static * Static : 자바 언어 예약어로 클래스 메서드와 클래스 필드를 지정할 때 사용한다. 인스턴스를 생성하지 않아도 클래스의 메서드와 필드에 접근을 가능하게 한다. * Static 함수, 변수의 메&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bambookim.tistory.com/17&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bambookim.tistory.com/17&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715915986847&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Java] 인스턴스 멤버 vs. 정적 멤버와 static&quot; data-og-description=&quot;인스턴스 멤버 인스턴스 멤버는 객체(인스턴스)를 생성한 뒤 사용할 수 있는 필드와 메소드이다. 인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다. public cl&quot; data-og-host=&quot;bambookim.tistory.com&quot; data-og-source-url=&quot;https://bambookim.tistory.com/17&quot; data-og-url=&quot;https://bambookim.tistory.com/17&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgxe8r/hyV6dIACZD/aKvmJCj8EJh0id1zXbfCSk/img.png?width=800&amp;amp;height=489&amp;amp;face=0_0_800_489,https://scrap.kakaocdn.net/dn/c6gNLq/hyV6knr3MT/iTQ7FrSQregYX7WsLW2VrK/img.png?width=800&amp;amp;height=489&amp;amp;face=0_0_800_489,https://scrap.kakaocdn.net/dn/kpFDP/hyV6lmm9T7/akmqzsCsguTnzPdfQJoKb0/img.png?width=1524&amp;amp;height=932&amp;amp;face=0_0_1524_932&quot;&gt;&lt;a href=&quot;https://bambookim.tistory.com/17&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bambookim.tistory.com/17&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgxe8r/hyV6dIACZD/aKvmJCj8EJh0id1zXbfCSk/img.png?width=800&amp;amp;height=489&amp;amp;face=0_0_800_489,https://scrap.kakaocdn.net/dn/c6gNLq/hyV6knr3MT/iTQ7FrSQregYX7WsLW2VrK/img.png?width=800&amp;amp;height=489&amp;amp;face=0_0_800_489,https://scrap.kakaocdn.net/dn/kpFDP/hyV6lmm9T7/akmqzsCsguTnzPdfQJoKb0/img.png?width=1524&amp;amp;height=932&amp;amp;face=0_0_1524_932');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Java] 인스턴스 멤버 vs. 정적 멤버와 static&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인스턴스 멤버 인스턴스 멤버는 객체(인스턴스)를 생성한 뒤 사용할 수 있는 필드와 메소드이다. 인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다. public cl&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bambookim.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@yyy96/static&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@yyy96/static&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715917526717&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;static 과 객체지향적 관점&quot; data-og-description=&quot;static 에 관하여. 그리고 자바가 static을 지양하는 이유는?&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@yyy96/static&quot; data-og-url=&quot;https://velog.io/@yyy96/static&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Z8fbs/hyV6akNyUT/kS1GQ05PmgOmQ0Rff4PCr1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bRJpYI/hyV6hKZAfA/9J31xchad7SI5OWh8RcXmk/img.jpg?width=3132&amp;amp;height=3132&amp;amp;face=0_0_3132_3132,https://scrap.kakaocdn.net/dn/bnRNRX/hyV6fNarhd/KLCXQKs8sC3qIVGYoJRzyK/img.png?width=840&amp;amp;height=425&amp;amp;face=0_0_840_425&quot;&gt;&lt;a href=&quot;https://velog.io/@yyy96/static&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@yyy96/static&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Z8fbs/hyV6akNyUT/kS1GQ05PmgOmQ0Rff4PCr1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bRJpYI/hyV6hKZAfA/9J31xchad7SI5OWh8RcXmk/img.jpg?width=3132&amp;amp;height=3132&amp;amp;face=0_0_3132_3132,https://scrap.kakaocdn.net/dn/bnRNRX/hyV6fNarhd/KLCXQKs8sC3qIVGYoJRzyK/img.png?width=840&amp;amp;height=425&amp;amp;face=0_0_840_425');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;static 과 객체지향적 관점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;static 에 관하여. 그리고 자바가 static을 지양하는 이유는?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <author>dev-rootable</author>
      <guid isPermaLink="true">https://dev-rootable.tistory.com/157</guid>
      <comments>https://dev-rootable.tistory.com/157#entry157comment</comments>
      <pubDate>Fri, 17 May 2024 12:49:45 +0900</pubDate>
    </item>
  </channel>
</rss>