<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>꾸준함의 미더덕</title>
    <link>https://goalinnext.tistory.com/</link>
    <description>Love what you do</description>
    <language>ko</language>
    <pubDate>Fri, 31 Jul 2026 03:28:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>꾸준함의 미더덕</managingEditor>
    <image>
      <title>꾸준함의 미더덕</title>
      <url>https://tistory1.daumcdn.net/tistory/4322568/attach/8f46193764c54abba3e8b5045fd3a36e</url>
      <link>https://goalinnext.tistory.com</link>
    </image>
    <item>
      <title>자연키 기반 복합키 VS 인조키 기반 단일키 PK 비교</title>
      <link>https://goalinnext.tistory.com/219</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글에선 자연키 기반 복합키와 인조키 기반 단일키를 PK로 사용하는 상황에 대해 비교해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자연키 기반 복합키 VS 인조키 기반 단일키&lt;/h2&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;1. 자연키 기반 복합키란?&lt;/b&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 인조키 기반  단일키란?&lt;/b&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;예상하는 상황) 복합키가 더 빠르다?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 생각을 해보자면 복합키가 더 빠르다고 생각할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'복합 인덱스가 더 빠르다'라는 문구가 익숙하기 때문일 것 같은데요. &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;복합 PK키와 복합 인덱스키를 구분해야합니다.&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;i&gt;복수의 조회 조건에 인덱스를 태울 수 있기 때문&lt;/i&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 '복합키(&lt;span style=&quot;color: #ef5369;&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;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;복합 PK키&lt;/b&gt;&lt;/span&gt;에 대해 생각해보자면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;복합키든 단일키든 PK로 조회 시 어차피 모든 조회 조건에 인덱스가 태워진 것&lt;/span&gt;&lt;/i&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;조회 조건을 키가 두개인 복합키인 PK로 조회되는 경우&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;첫 번째 조회 조건을 인덱스로 검색하여 검색대상의 후보를 절반씩 줄여가며 시간복잡도 O(logn)으로 조회할 수 있을 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 조회 대상 중 나머지 조회 조건으로 검색을 해본다면 동일하게 후보를 절반씩 줄여가며 시간복잡도 O(logn)으로 조회할 것입니다.&lt;/p&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: #ef5369;&quot;&gt;결국 단일키로 조회할 수 있는 시간복잡도 O(logn)과 동일한 것&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;실제 성능 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 데이터베이스 &lt;span&gt;9.1.0 버전에서  검증을 진행했습니다.&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;아래와 같은 ddl문으로 각각의 테이블을 생성합니다.&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;700&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkxC6b/btsLOM1FDA7/aQxkJinkPeXYJDIIm1CGE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkxC6b/btsLOM1FDA7/aQxkJinkPeXYJDIIm1CGE1/img.png&quot; data-alt=&quot;자연키 기반 복합키 테이블&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkxC6b/btsLOM1FDA7/aQxkJinkPeXYJDIIm1CGE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkxC6b%2FbtsLOM1FDA7%2FaQxkJinkPeXYJDIIm1CGE1%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;323&quot; height=&quot;182&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;394&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;682&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGyC4i/btsLODqbl5w/B4i1UloyqgJK2QgEQksitK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGyC4i/btsLODqbl5w/B4i1UloyqgJK2QgEQksitK/img.png&quot; data-alt=&quot; 인조키 기반 단일키 테이블&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGyC4i/btsLODqbl5w/B4i1UloyqgJK2QgEQksitK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGyC4i%2FbtsLODqbl5w%2FB4i1UloyqgJK2QgEQksitK%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;316&quot; height=&quot;199&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 인조키 기반 단일키 테이블&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합키는 자연키 기반이므로 &lt;span style=&quot;color: #ef5369;&quot;&gt;실제 비즈니스에서 쓰이는 데이터가 키&lt;/span&gt;가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그렇기 때문에 성능에 영향을 줄 거라고 생각했으나 영향 없었음 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;2516&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ObtJb/btsLNWD1bcU/tpMI0ZKehnRwkfQdAEoh11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ObtJb/btsLNWD1bcU/tpMI0ZKehnRwkfQdAEoh11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ObtJb/btsLNWD1bcU/tpMI0ZKehnRwkfQdAEoh11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FObtJb%2FbtsLNWD1bcU%2FtpMI0ZKehnRwkfQdAEoh11%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;2516&quot; height=&quot;362&quot; data-origin-width=&quot;2516&quot; data-origin-height=&quot;362&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;150만개 정도의 데이터 상황에선 조회 속도 거의 유사했습니다.&lt;/p&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;데이터의 건수가 많으면 달라지지 않을까 싶어서 1000만개의 데이터를 넣어서 비교해보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blL0Ky/btsLM3jBG4E/ZmVhfBJ1Nc4ofe2Hrl5dn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blL0Ky/btsLM3jBG4E/ZmVhfBJ1Nc4ofe2Hrl5dn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blL0Ky/btsLM3jBG4E/ZmVhfBJ1Nc4ofe2Hrl5dn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblL0Ky%2FbtsLM3jBG4E%2FZmVhfBJ1Nc4ofe2Hrl5dn0%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;2574&quot; height=&quot;396&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;396&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;1000만개 정도의 데이터 상황에서도 조회 속도가 차이가 없었습니다.&lt;/p&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;pre id=&quot;code_1736910832093&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;query_block&quot;: {
    &quot;select_id&quot;: 1,
    &quot;cost_info&quot;: {
      &quot;query_cost&quot;: &quot;1.00&quot;
    },
    &quot;table&quot;: {
      &quot;table_name&quot;: &quot;blog&quot;,
      &quot;access_type&quot;: &quot;const&quot;,
      &quot;possible_keys&quot;: [
        &quot;PRIMARY&quot;
      ],
      &quot;key&quot;: &quot;PRIMARY&quot;,
      &quot;used_key_parts&quot;: [
        &quot;blog_url&quot;,
        &quot;rss_url&quot;
      ],
      &quot;key_length&quot;: &quot;2044&quot;,
      &quot;ref&quot;: [
        &quot;const&quot;,
        &quot;const&quot;
      ],
      &quot;rows_examined_per_scan&quot;: 1,
      &quot;rows_produced_per_join&quot;: 1,
      &quot;filtered&quot;: &quot;100.00&quot;,
      &quot;using_index&quot;: true,
      &quot;cost_info&quot;: {
        &quot;read_cost&quot;: &quot;0.00&quot;,
        &quot;eval_cost&quot;: &quot;0.10&quot;,
        &quot;prefix_cost&quot;: &quot;0.00&quot;,
        &quot;data_read_per_join&quot;: &quot;2K&quot;
      },
      &quot;used_columns&quot;: [
        &quot;blog_url&quot;,
        &quot;rss_url&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;pre id=&quot;code_1736910850710&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;query_block&quot;: {
    &quot;select_id&quot;: 1,
    &quot;cost_info&quot;: {
      &quot;query_cost&quot;: &quot;1.00&quot;
    },
    &quot;table&quot;: {
      &quot;table_name&quot;: &quot;blog2&quot;,
      &quot;access_type&quot;: &quot;const&quot;,
      &quot;possible_keys&quot;: [
        &quot;PRIMARY&quot;
      ],
      &quot;key&quot;: &quot;PRIMARY&quot;,
      &quot;used_key_parts&quot;: [
        &quot;id&quot;
      ],
      &quot;key_length&quot;: &quot;4&quot;,
      &quot;ref&quot;: [
        &quot;const&quot;
      ],
      &quot;rows_examined_per_scan&quot;: 1,
      &quot;rows_produced_per_join&quot;: 1,
      &quot;filtered&quot;: &quot;100.00&quot;,
      &quot;cost_info&quot;: {
        &quot;read_cost&quot;: &quot;0.00&quot;,
        &quot;eval_cost&quot;: &quot;0.10&quot;,
        &quot;prefix_cost&quot;: &quot;0.00&quot;,
        &quot;data_read_per_join&quot;: &quot;2K&quot;
      },
      &quot;used_columns&quot;: [
        &quot;id&quot;,
        &quot;blog_url&quot;,
        &quot;rss_url&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;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;조회 속도는 동일한 데 비하여, &lt;br /&gt;자연키 기반 복합키의 인덱스 길이가 훨씬 긴 것을 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론) 자연키 기반 복합키(PK)는 장점이 없다&lt;/h2&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: #ef5369;&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;span style=&quot;color: #ef5369;&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;span style=&quot;color: #ef5369;&quot;&gt;인덱스의 길이&lt;/span&gt;가 증가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 비즈니스 의미를 지닌 데이터가 자동증가값보다 더 크기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 복합키이므로 전체 인덱스의 길이 또한 * N개 되어 증가됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&lt;span style=&quot;color: #ef5369;&quot;&gt; DB 메모리 관리 측면에서 부담&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;단순 GET 요청에도 길이가 긴 복수개의 키를 URL에 담아야하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;span style=&quot;color: #ef5369;&quot;&gt;개발 생산성 측면에서도 좋지 못합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론의 결론)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; PK에는 자연키 기반 복합키를 사용하지 말자.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;혹시라도 제가 놓친 부분 혹은 다른 의견 있으시다면 댓글로 남겨주세요!&lt;/span&gt;&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/219</guid>
      <comments>https://goalinnext.tistory.com/219#entry219comment</comments>
      <pubDate>Tue, 14 Jan 2025 19:04:32 +0900</pubDate>
    </item>
    <item>
      <title>동기인듯 동기 아닌 동기 같은 너 - 비동기 블로킹 (Spring MVC에서 Flux 사용하기 by WebClient)</title>
      <link>https://goalinnext.tistory.com/217</link>
      <description>&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;이번 시간엔 Spring MVC 프로젝트에서 WebClient를 사용 시 내부 동작이 어떻게 이루어지는 지에 대해 알아보도록 하겠습니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Spring MVC (톰캣 -서블릿 - 동기 멀티스레드) VS Spring Web Flux (네티 - 이벤트루프-비동기 단일/멀티스레드)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 해당 상황을 이해하기 위해 Spring MVC의 기술적 바탕과 WebClient를 사용하기 위한 Spring WebFlux의 기술적 바탕이 다르다는 사실을 인지하고 있어야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Spring MVC - 톰캣 &amp;amp; 서블릿 기반의 동기 멀티스레드 기술&lt;/h2&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;Spring MVC는 톰캣 서블릿 기반의 동기 멀티스레드&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;톰캣 컨테이너는 스레드 풀에 미리 스레드를 생성시켜두고 HTTP 요청 마다 서블릿에 스레드를 할당하여 요청을 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 I/O 발생 시 스레드는 &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;&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;1546&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckZKIw/btsLwrDvMch/TpPAtKzkplWMY1bKZobVo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckZKIw/btsLwrDvMch/TpPAtKzkplWMY1bKZobVo1/img.png&quot; data-alt=&quot;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckZKIw/btsLwrDvMch/TpPAtKzkplWMY1bKZobVo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckZKIw%2FbtsLwrDvMch%2FTpPAtKzkplWMY1bKZobVo1%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;1546&quot; height=&quot;872&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. WebFlux - 네티 &amp;amp; 이벤트 루프 기반의 비동기 단일/멀티 스레드 기술&lt;/h2&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;Spring&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;WebFlux는 &lt;b&gt;Netty와 이벤트 루프 기반의 비동기 방식으로&lt;/b&gt; 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;HTTP 요청을 처리하는 스레드는 작업을 비동기로 즉시 위임하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;I/O 작업이 발생하면 스레드는 &lt;b&gt;논블로킹&lt;/b&gt;으로 해당 작업을 이벤트 루프에 할당하고, 응답이 도착하면 비동기 방식으로 후속 작업을 처리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ztYtK/btsLwtg18KU/Ia3wSkh5AGcoUsFG6p8a40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ztYtK/btsLwtg18KU/Ia3wSkh5AGcoUsFG6p8a40/img.png&quot; data-alt=&quot;https://www.stefankreidel.io/blog/spring-webflux&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ztYtK/btsLwtg18KU/Ia3wSkh5AGcoUsFG6p8a40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FztYtK%2FbtsLwtg18KU%2FIa3wSkh5AGcoUsFG6p8a40%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;1540&quot; height=&quot;882&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.stefankreidel.io/blog/spring-webflux&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. Spring MVC + WebFlux = ?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 SpringMVC와 WebFlux는 기반 기술이 다른 것을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 우리는 SpringMVC 프로젝트에서 WebFlux의 라이브러리인 WebClient를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 경우 서버 구조는 어떻게 작동되는 것일까요?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHGeIb/btsLvzvyFAy/poifeZa9nkAWeMp5y5kdZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHGeIb/btsLvzvyFAy/poifeZa9nkAWeMp5y5kdZ0/img.png&quot; data-alt=&quot;https://docs.spring.io/spring-framework/reference/web/webflux.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHGeIb/btsLvzvyFAy/poifeZa9nkAWeMp5y5kdZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHGeIb%2FbtsLvzvyFAy%2FpoifeZa9nkAWeMp5y5kdZ0%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;1428&quot; height=&quot;542&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.spring.io/spring-framework/reference/web/webflux.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 웹플럭스 문서를 보면 위의 두 기술은 공존할 수 있다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 프로젝트 내에  Spring MVC 와 Spring WebFlux의 두가지 의존성을 추가하면&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6e18H/btsLwcs9Z1V/CHzUuew9WziLuwFCg36JuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6e18H/btsLwcs9Z1V/CHzUuew9WziLuwFCg36JuK/img.png&quot; data-alt=&quot;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6e18H/btsLwcs9Z1V/CHzUuew9WziLuwFCg36JuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6e18H%2FbtsLwcs9Z1V%2FCHzUuew9WziLuwFCg36JuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;870&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;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;앞에서 스프링 MVC는 I/O 발생 시 해당 블로킹되어 응답을 기다린다고 했었죠?&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;이 때 WebFlux 기반인 WebClient를 사용하면&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;작업을 이벤트 루프에 할당&lt;/b&gt;하여&lt;/span&gt;&amp;nbsp;블로킹이 아닌 &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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 하지만 결국 일반적인 DB I/O 요청 상황에선 비동기 블로킹으로 처리되어 결국 동기적인 방식으로 응답된다.&lt;/h2&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: #ee2323;&quot;&gt;&lt;b&gt;&lt;i&gt;하지만 문제가 있습니다.&amp;nbsp;&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 어플리케이션이 비동기적으로 즉시 응답을 하여도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;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;특히 대부분의 서비스 어플리케이션은 DB를 의존하고 있을텐데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 DB와 구현 기술 대부분 동기로 처리된다는 문제가 있습니다.&lt;/p&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; 이를 지원하는 R2DBC나 Nosql 환경에선 비동기적으로 활용 가능&lt;/span&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 코드로 확인하기!&lt;/h2&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;1. HTTP 요청 시 톰캣 스레드 풀에서 스레드 하나 할당 받음.&lt;/p&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. WebClient 요청 시 논블로킹으로 요청 (톰캣 스레드)&lt;/p&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. 하지만 응답을 받기 위해 결국 블로킹되어 대기 (톰캣 스레드)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;- 요청을 논블로킹으로 보냈지만 동기적인 응답을 받기 위해 대기..&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;- 요청을 보내고 다른 작업을 하다가 응답을 받을 수는 있음 !!!&lt;/i&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;4. 응답을 이벤트 루프에 네티 스레드로 받음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 최종 결과값을 톰캣 스레드가 받아 처리함&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1. 환경 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Spring MVC와 Spring WebFlux의 의존성을 동시에 추가해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cijNsI/btsLvKjAEgK/hPOtep42MoSqNP771Ex5k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cijNsI/btsLvKjAEgK/hPOtep42MoSqNP771Ex5k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cijNsI/btsLvKjAEgK/hPOtep42MoSqNP771Ex5k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcijNsI%2FbtsLvKjAEgK%2FhPOtep42MoSqNP771Ex5k0%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;1940&quot; height=&quot;622&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;622&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;프로젝트 구동 시 Netty 이벤트 루프 스레드(reactor-http-nio 스레드)들이 생성된 것도 확인할 수 있습니다!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4XmZs/btsLp7N1oik/7BVyPsHb2gHflWa8I5wtO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4XmZs/btsLp7N1oik/7BVyPsHb2gHflWa8I5wtO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4XmZs/btsLp7N1oik/7BVyPsHb2gHflWa8I5wtO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4XmZs%2FbtsLp7N1oik%2F7BVyPsHb2gHflWa8I5wtO1%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;1666&quot; height=&quot;570&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;주의) Netty는 단일 스레드라며 ?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Netty가 단일 스레드라는 설명은 요청을 수신하는 Acceptor Thread가 단일 스레드로 설정할 수 있다는 것이며, 그 외의 네티를 구성하는 이벤트 루프 등에 대한 여러 Worker Thread가 별도로 존재한다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-2. 클라이언트로부터 HTTP 요청 받은 상황 -&amp;nbsp; 톰캣 스레드 풀의 스레드가 할당됨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣 스레드 풀의 스레드가 할당되어 요청을 받은 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2724&quot; data-origin-height=&quot;1608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZinH0/btsLvyRcXsL/7fV9b8wpMa048MysbG9Oa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZinH0/btsLvyRcXsL/7fV9b8wpMa048MysbG9Oa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZinH0/btsLvyRcXsL/7fV9b8wpMa048MysbG9Oa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZinH0%2FbtsLvyRcXsL%2F7fV9b8wpMa048MysbG9Oa0%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;2724&quot; height=&quot;1608&quot; data-origin-width=&quot;2724&quot; data-origin-height=&quot;1608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-3. WebClient로 다른 서버에 HTTP 요청 - Netty 이벤트 루프 스레드가 할당 됨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔을 보면 응답을 보내는 즉시 콘솔에 프린트를 찍었습니다. 이로서 해당 작업이 비동기적으로 진행된다는 것을 알았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 디버그 툴이 아래의 라인에서 멈추었는데 해당 스레드 명이 &quot;reactor-http-nio-4&quot;로 Netty 이벤트 루프 스레드인 것을 알 수 있습니다!&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;2704&quot; data-origin-height=&quot;1588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvRct/btsLt6hbTz1/Y7WaLad0AMHEX1R5NTClVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvRct/btsLt6hbTz1/Y7WaLad0AMHEX1R5NTClVK/img.png&quot; data-alt=&quot;콘솔에 숫자가 찍힌 것을 보면 확실히 비동기가 맞다 !!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvRct/btsLt6hbTz1/Y7WaLad0AMHEX1R5NTClVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvvRct%2FbtsLt6hbTz1%2FY7WaLad0AMHEX1R5NTClVK%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;2704&quot; height=&quot;1588&quot; data-origin-width=&quot;2704&quot; data-origin-height=&quot;1588&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;하지만 위에서 언급 드렸듯이 많은 상황에선 I/O의 응답을 블로킹하여 기다려야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답값이 Flux면 toStream(), Mono면 block()으로 톰캣의 스레드를 블로킹하여 응답을 대기합니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;5-4. 톰캣 스레드가 블로킹되어 대기하다 WebClient의 응답 값을 받음&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2710&quot; data-origin-height=&quot;1626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuv2KG/btsLuH8TUb2/r2xDlcSkSWamLlzYc2vQ8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuv2KG/btsLuH8TUb2/r2xDlcSkSWamLlzYc2vQ8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuv2KG/btsLuH8TUb2/r2xDlcSkSWamLlzYc2vQ8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuv2KG%2FbtsLuH8TUb2%2Fr2xDlcSkSWamLlzYc2vQ8K%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;2710&quot; height=&quot;1626&quot; data-origin-width=&quot;2710&quot; data-origin-height=&quot;1626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebClient의 응답을 받은 톰캣 스레드는 다시 &quot;http-nio-8080-exec-6&quot;임을 확인해볼 수 있습니다.&lt;/p&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;해당 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;톰캣&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;스레드는&lt;span&gt; &lt;/span&gt;&lt;/span&gt;블로킹되어 대기하다 응답을 최종 리턴 받은 것입니다 !!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;6. 결론) &quot;비동기 블로킹&quot;은 결국 동기적으로 응답된다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Spring MVC에서 일반적인 동기 방식으로 WebClient를 사용할 시 해당 기술을 &quot;&lt;b&gt;비동기 블로킹&lt;/b&gt; &quot;으로 사용하는 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(비동기적으로 println()를 찍었지만 결국 WebClient의 응답을 블로킹하여 대기하였음)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 이는 결국 &lt;b&gt;사용자에게 동기적인 형태로 응답&lt;/b&gt;하는 것으로 이해해볼 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총총&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.stefankreidel.io/blog/spring-webmvc-with-webclient&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://moonsiri.tistory.com/187&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://moonsiri.tistory.com/187&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@ksiisk99/spring2&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@ksiisk99/spring2&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://findmypiece.tistory.com/276&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://findmypiece.tistory.com/276&lt;/a&gt;&lt;/p&gt;</description>
      <category>백엔드/Spring</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/217</guid>
      <comments>https://goalinnext.tistory.com/217#entry217comment</comments>
      <pubDate>Tue, 24 Dec 2024 15:25:30 +0900</pubDate>
    </item>
    <item>
      <title>자바/스프링 개발자를 위한 실용주의 프로그래밍 8장/9장 - 레이어드 아키텍처</title>
      <link>https://goalinnext.tistory.com/218</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;8장 레이어드 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;레이어드&amp;nbsp;아키텍처는&amp;nbsp;구체적인&amp;nbsp;사양이&amp;nbsp;아님.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;레이어드 아키텍처의 조건 (제약)&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;레이어&amp;nbsp;구조를&amp;nbsp;사용&amp;nbsp;(필수)&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;레이어&amp;nbsp;간&amp;nbsp;의존&amp;nbsp;방향은&amp;nbsp;단방향&amp;nbsp;(필수)&amp;nbsp;&amp;nbsp;&lt;br /&gt;- 레이어 간 통신은 인접한 레이어에서만 이뤄지게 함 -&amp;gt; 작가는 필수는 아니라 생각함&amp;nbsp;&amp;nbsp;&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;아키텍처란? - 제약&amp;nbsp;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;아키텍트들이&amp;nbsp;아키텍처를&amp;nbsp;설명할&amp;nbsp;때&amp;nbsp;항상&amp;nbsp;빠지지&amp;nbsp;않고&amp;nbsp;말하는&amp;nbsp;한&amp;nbsp;가지&amp;nbsp;특징은&amp;nbsp;제약조건&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;아키텍처는&amp;nbsp;제약&amp;nbsp;조건을&amp;nbsp;이용해&amp;nbsp;개발자가&amp;nbsp;해도&amp;nbsp;되는&amp;nbsp;것과&amp;nbsp;하지&amp;nbsp;말아야&amp;nbsp;하는&amp;nbsp;것을&amp;nbsp;결정한다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;더&amp;nbsp;나아가,&amp;nbsp;해서는&amp;nbsp;안되는&amp;nbsp;일이&amp;nbsp;개발&amp;nbsp;단계에서&amp;nbsp;일어나지&amp;nbsp;않게&amp;nbsp;원천적으로&amp;nbsp;차단함.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;제약 - 목적을 달성하기 위한 것&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- 제약 조건은 목적에 따라 변경될 수 있음&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잘못된 레이어드 아키텍처&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JPA 엔티티 우선 접근 개발 방식 vs 엔드포인트 우선 접근 개발 방식&amp;nbsp;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;JPA&amp;nbsp;먼저&amp;nbsp;떠올렸다는&amp;nbsp;것은&amp;nbsp;DDL을&amp;nbsp;고민했다는&amp;nbsp;것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 데이터 종속&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 데이터 위주의 사고방식의 문제가 동일하게 생김&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;잘못된&amp;nbsp;레이어드&amp;nbsp;아키텍처는&amp;nbsp;레이어를&amp;nbsp;지나치게&amp;nbsp;추상화함&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;gt; 인프라스트럭처 레이어를 먼저 만들면 어떤 문제가 있을까 ?&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;API&amp;nbsp;엔드포인트&amp;nbsp;우선&amp;nbsp;접근&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt;&amp;nbsp;스프링&amp;nbsp;종속&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;시스템을&amp;nbsp;도메인&amp;nbsp;요구사항&amp;nbsp;관점에서&amp;nbsp;봤다는&amp;nbsp;것에서&amp;nbsp;JPA&amp;nbsp;우선&amp;nbsp;방식보다는&amp;nbsp;낫다..&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;하지만&amp;nbsp;이런&amp;nbsp;방식은&amp;nbsp;프레임워크&amp;nbsp;종속적임&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- API 엔드포인트를 고민하는 것은 도메인 요구사항이 무엇인지 파악하는데 도움을 주는 단계 뿐임&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;gt; 요청 응답형식 먼저 개발하는 것도 데이터 위주의 생각을 할 가능성이 높다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애플리케이션의 본질은 도메인이다&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;애플리케이션을&amp;nbsp;개발한다는&amp;nbsp;것은&amp;nbsp;도메인을&amp;nbsp;파악하고,&amp;nbsp;이에&amp;nbsp;따른&amp;nbsp;도메인&amp;nbsp;모델을&amp;nbsp;구성하고,&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;-&amp;gt;&amp;nbsp;도메인&amp;nbsp;모델을&amp;nbsp;표현하는데&amp;nbsp;적합한&amp;nbsp;언어를&amp;nbsp;선택하고,&amp;nbsp;도메인&amp;nbsp;모델을&amp;nbsp;만들고,&amp;nbsp;도메인&amp;nbsp;기능을&amp;nbsp;제공할&amp;nbsp;기술을&amp;nbsp;선택한다는&amp;nbsp;것임.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;진화하는 아키텍처 - 비즈니스 레이어 -&amp;gt; 어플리케이션 + 도메인 레이어&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;레이어드&amp;nbsp;아키텍처를&amp;nbsp;사용하면서도&amp;nbsp;객체지향을&amp;nbsp;챙길&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;gt; 시스템 개발의 첫 시작을 도메인으로 두기 !&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;도메인을&amp;nbsp;개발하려면&amp;nbsp;비즈니스&amp;nbsp;레이어&amp;nbsp;부터&amp;nbsp;개발하자&amp;nbsp;&amp;nbsp;&lt;br /&gt;cf) 비즈니스 레이어 = 서비스 컴포넌트 + 도메인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 비즈니스 레이어 중에서도 도메인을 가장 먼저 개발해야함.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;비즈니스&amp;nbsp;레이어안에&amp;nbsp;도메인이&amp;nbsp;존재해서&amp;nbsp;인지&amp;nbsp;모델에&amp;nbsp;혼란이&amp;nbsp;발생&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;gt; 도메인 레이어를 분리하자 : 서비스 레이어 = 어플리케이션 레이어 + 도메인 레이어&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;프레젠테이션&amp;nbsp;-&amp;gt;&amp;nbsp;어플리케이션&amp;nbsp;-&amp;gt;&amp;nbsp;도메인&amp;nbsp;-&amp;gt;&amp;nbsp;인프라스트럭처&amp;nbsp;레이어&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도메인 레이어는 순수 자바 코드로 작성 하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 외부 라이브러리에 의존하지 않고 자유롭게 만들기 위함&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 애플리케이션 서비스는 단순하게 도메인에 있는 코드를 실행하는 역할은 함!!&amp;nbsp;&lt;br /&gt;-&amp;gt; '서비스는 J2EE패턴중 하나인 비즈니스 서비스 파사드처럼 사용될 수 있다'&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;gt; 애플리케이션 레이어에 위치한 &lt;b&gt;서비스&lt;/b&gt; 컴포넌트는 이제 &lt;b&gt;도메인 레이어의 파사드&lt;/b&gt;가 됐다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Account &amp;amp; AccountJpaEntity&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;순수한&amp;nbsp;도메인&amp;nbsp;모델과&amp;nbsp;영속성&amp;nbsp;객체를&amp;nbsp;사용하는&amp;nbsp;애플리케이션&amp;nbsp;서비스&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734848472750&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface AccountRepository {
    public Account findById(long id);

    public void save(Account account);
}

@Builder
@RequiredArgsConstructor
public class Account {
    public final Long id;
    public final String email;
    public final String nickname;

    public Account withNickname(String nickname) { //불변객체수정  
        return Account.builder().is(this.id).email(this.email).nickname(nickname).build();
    }
}

@Service
@RequiredArgsConstructor
public class AccountService {
    private final AccountRepository accountRepository;

    /**
     * 이렇게 되면 서비스 레이어에서 JPA 기능을 사용하지 않는 것..
     */
    @Transactional
    public Account updateNicknameById(long id, String nickname) {
        Account account = accountRepository.findById(id);
        account = account.withNickname(nickname);
        accountRepository.save(account);
    }
}

@Repository
@RequiredArgsConstructor
public class AccountRepositoryImpl implements AccountRepository {
    private final AccountJpaRepository accountJpaRepository;

    @Override
    public Account findById(long id) {
        return accountJpaRepository.findById(id).orElseThrow(() -&amp;gt; new NotFoundException(&quot;account&quot;, id)).toModel();
    }

    @Override
    @Transactional
    public void save(Account account) {
        accountJpaRepository.save(AccountJpaEntity.from(account));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;현재&amp;nbsp;구조에서는&amp;nbsp;도메인&amp;nbsp;레이어가&amp;nbsp;인프라스트럭처&amp;nbsp;레이어를&amp;nbsp;참조하지&amp;nbsp;않는다&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;-&amp;gt;&amp;nbsp;도메인&amp;nbsp;레이어는&amp;nbsp;JPA나&amp;nbsp;스프링을&amp;nbsp;임포트하지&amp;nbsp;않는다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;-&amp;nbsp;개인적인&amp;nbsp;의견&amp;nbsp;:&amp;nbsp;그것은&amp;nbsp;조회를&amp;nbsp;비즈니스&amp;nbsp;로직(도메인&amp;nbsp;로직)으로&amp;nbsp;보지&amp;nbsp;않았을&amp;nbsp;경우&amp;nbsp;아닌가?&lt;br /&gt;- 조회를 복잡한 비즈니스 로직으로 본다면 어떻게 되는 것일까???&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;도메인&amp;nbsp;모델을&amp;nbsp;인터페이스로&amp;nbsp;작성하면&amp;nbsp;도메인&amp;nbsp;서비스에서&amp;nbsp;DB의존하더라도&amp;nbsp;기술을&amp;nbsp;의존하지&amp;nbsp;않을&amp;nbsp;수&amp;nbsp;있지&amp;nbsp;않을까?&amp;nbsp;&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;JPA와의 결합 끊기&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;어플리케이션&amp;nbsp;레이어에&amp;nbsp;위치한&amp;nbsp;AccountService&amp;nbsp;컴포넌트가&amp;nbsp;인프라스트럭처&amp;nbsp;레이어&amp;nbsp;위에&amp;nbsp;JPA에&amp;nbsp;의존하는&amp;nbsp;상황을&amp;nbsp;제거하기&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;어플리케이션&amp;nbsp;레이어에서&amp;nbsp;Repository&amp;nbsp;라는&amp;nbsp;인터페이스를&amp;nbsp;새롭게&amp;nbsp;배치하기&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 프레임워크와의 결합 끊기&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;AccountService를&amp;nbsp;인터페이스로&amp;nbsp;-&amp;gt;&amp;nbsp;헥사고날&amp;nbsp;아키텍처가&amp;nbsp;된다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;헥사고날&amp;nbsp;아키텍처는&amp;nbsp;외부세계를&amp;nbsp;다루는&amp;nbsp;방식으로&amp;nbsp;포트-어댑터&amp;nbsp;패턴을&amp;nbsp;사용함&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;-&amp;gt;&amp;nbsp;실은&amp;nbsp;의존성&amp;nbsp;역전을&amp;nbsp;사용함&amp;nbsp;-&amp;gt;&amp;nbsp;의존성&amp;nbsp;역전의&amp;nbsp;또&amp;nbsp;다른&amp;nbsp;이름이&amp;nbsp;포트-어댑터&amp;nbsp;패&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;스프링&amp;nbsp;웹과&amp;nbsp;끊은&amp;nbsp;것이지&amp;nbsp;스프링과&amp;nbsp;끊은&amp;nbsp;것은&amp;nbsp;아님&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;하지만&amp;nbsp;프레젠테이션&amp;nbsp;레이어의&amp;nbsp;코드는&amp;nbsp;보통&amp;nbsp;재사용될&amp;nbsp;수&amp;nbsp;없음&amp;nbsp;-&amp;gt;&amp;nbsp;의존성&amp;nbsp;역전&amp;nbsp;적용했을&amp;nbsp;때&amp;nbsp;이점이&amp;nbsp;작음&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빈약한 도메인&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;아키텍처의&amp;nbsp;효과가&amp;nbsp;없다면&amp;nbsp;도메인이&amp;nbsp;빈약한게&amp;nbsp;없을까.&amp;nbsp;빈약한&amp;nbsp;도메인이&amp;nbsp;성공할&amp;nbsp;수&amp;nbsp;있을까&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/218</guid>
      <comments>https://goalinnext.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 22 Dec 2024 15:21:21 +0900</pubDate>
    </item>
    <item>
      <title>컴퓨터 맨 밑바닥의 비밀 - 2.1 콜백함수, 동기화, 비동기화, 블로킹, 논블로킹</title>
      <link>https://goalinnext.tistory.com/211</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;콜백함수&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;함수를 변수처럼&lt;/span&gt; 사용하는 개념
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분기문으로 다른 로직을 담긴 함수들을 선언하여 처리하기 어려운 상황에 유용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void make_donut(func f){
    //..
    f();
    //..
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;make_donut 함수를 사용하고 싶은 프로그래머는 자신이 정의한 현지화 함수를 전달
&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: #ef5369;&quot;&gt;함수 변수를 콜백 함수&lt;/span&gt;라고 함&lt;/b&gt;&lt;/li&gt;
&lt;li&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;/ul&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;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콜백 함수의 처리 속도가 오래걸리는 상황인데 이를 기다려줄 수 없다면?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731019430880&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 호출 스레드
make_donut(formed_D); // 아래 작업을 기다려줄 수 없을 때 -&amp;gt; 비동기 콜백함수를 사용하기
something_important(); // 기다려줄 수 없는 작업..&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;함수 내부에서 스레드를 생성하고 &lt;span style=&quot;color: #ef5369;&quot;&gt;새로운 스레드가 콜백함수를 처리&lt;/span&gt;&lt;/b&gt;하도록 하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void make_donut(func f){
    //별도 생성된 스레드가 콜백함수를 처리
    thread t(real_make_donut, f)
}
void real_make_donut(func f){
    //..
    f();
    //..
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주의할 점은 something_important()가 실행될 때 도넛 생성 작업은 아직 시작되지 않았을 수도 있다는 점
&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이와같이 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;호출 스레드가 콜백 함수 실행에 의존하지 않는 것을 비동기 콜백&lt;/b&gt;&lt;/span&gt;이라고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;request(handle); //handle 콜백함수를 비동기로 처리할 때
// -&amp;gt; request를 실행한 스레드와 handle을 실행한 스레드는 별개다&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;비동기 호출 프로그래밍 방식은 작업처리가 두 부분&lt;/b&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;request를 호출하고 handle을 넘겨주는 작업 (handle을 정의 했지만 비동기로 실행되기 때문에)&lt;/li&gt;
&lt;/ul&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;request 내부에서 처리되는 작업 (request 즉시 실행되지만 내부가 캡슐화 되었으므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;다른 코드에 매개변수로 전달되는 실행 가능한 코드&lt;/li&gt;
&lt;li&gt;콜백함수와 주 프로그램은 같은 계층에 있지만 해당 콜백함수를 작성만하지 직접 호출하지 않는다는 것&lt;/li&gt;
&lt;/ul&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;특정 &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;이 관점에서 콜백함수는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;이벤트 핸들러&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;콜백함수로 전달되는 이벤트 핸들러는 보통 비동기 콜백임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동기 콜백이면 이벤트 발행될 때까지 다른 작업 불가.. (while을 돌며 이벤트 발행여부를 확인 하고 있으므로..)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;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;동기 콜백 vs 비동기 콜백
&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;/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;콜백 지옥
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비즈니스가 복잡한 경우 콜백 지옥에 빠질 가능성이 높음&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 콜백의 효율과 동기 콜백의 단순성을 함께 누리는 방법이 코루틴&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;동기 vs 비동기&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;파일 입출력은 동기지만 다른 스레드에서 진행됨&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;read()를 호출하여 파일을 읽는 상황이라면 시스템 콜을 운영체제에 보내고 &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;블로킹 입출력&lt;/li&gt;
&lt;li&gt;이처럼 스레드가 여러개여도 동기일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;비동기 호출&lt;/b&gt;은 보통 시간이 많이 걸리는 &lt;b&gt;입출력 작업을 백그라운드 형태로 실행&lt;/b&gt;&lt;/span&gt;함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 읽고 쓰기, 네트워크 데이터 송수신, 데이터베이스 작업 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기 호출&lt;/b&gt; 방식에서 작업이 실제로 &lt;b&gt;완료되는 시점을 어떻게 파악&lt;/b&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;두 가지 상황이 존재
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;알림 작동 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;웹서버에서 동기와 비동기 작업&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹서버 요청 처리 작업 중 대표적인 것이 &lt;span style=&quot;color: #ef5369;&quot;&gt;데이터베이스 요청&lt;/span&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 외 입출력 없는 상황&lt;/li&gt;
&lt;li&gt;A,B,C 작업&lt;/li&gt;
&lt;li&gt;데이터베이스 요청&lt;/li&gt;
&lt;li&gt;D,E,F 작업&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일반적으로 이런 형태의 웹서버에는 &lt;span style=&quot;color: #ef5369;&quot;&gt;주 스레드와 데이터베이스 처리 스레드&lt;/span&gt;가 존재&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;동기 처리 시 주 스레드의 유휴 시간이 발생&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;비동기 구현&lt;/span&gt;에선 주 스레드가 데이터베이스 요청 전송 후 &lt;span style=&quot;color: #ef5369;&quot;&gt;바로 새로운 사용자 요청을 직접 처리&lt;/span&gt;함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주스레드가 A,B,C처리 후 데이터베이스 요청 후 새로운 사용자 요청을 받는다면 D,E,F는??&lt;/li&gt;
&lt;li&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: #ef5369;&quot;&gt;처리 결과를 신경쓰지 않음&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주 스레드가 아닌 데이터베이스 스레드가 데이터베이스 처리 후 이어서 D,E,F처리&lt;/li&gt;
&lt;li&gt;데이터베이스 스레드가 D,E,F 작업에 대해 어떻게 아는지?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주 스레드에서 D,E,F를 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;콜백함수&lt;/b&gt;&lt;/span&gt;로 넘긴다 (데이터 베이스 호출과 함께)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주 스레드가 데이터베이스 &lt;span style=&quot;color: #ef5369;&quot;&gt;처리 결과에 관심&lt;/span&gt;을 가질 때&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 스레드는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;알림작동 방식&lt;/b&gt;&lt;/span&gt;을 이용하여 작업 결과를 주 스레드로 전송&lt;/li&gt;
&lt;li&gt;주스레드는 메시지를 수신하면 후반부작업 처리&lt;/li&gt;
&lt;li&gt;데이터베이스 스레드가 유휴상태 (입출력 기간동안) 라는점을 제외하면 주스레드에 유휴시간은 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터베이스 스레드가 후반부작업을 처리하는 것보다는 비효율적이지만 동기보다는 효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;블로킹과 논블로킹&lt;/h4&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;함수A가 함수B를 호출할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;B를 호출함과 동시에 &lt;b&gt;운영체제가 함수A가 실행 중인 스레드나 프로세스를 일시 중지&lt;/b&gt; 시킨다면 블록킹&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;아니면 논블로킹&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수 호출로 인해 &lt;b&gt;호출자의 스레드나 프로세스가 운영체제에 의해 일시중지 되는 것은 어떤 경우??&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;블로킹의 핵심 문제 : 입출력&lt;/b&gt;&lt;/span&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cf) &lt;b&gt;디스크 입출력 행위는 cpu가 처리하지 않음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cpu는 입출력 요청을 디스크 컨트롤러에 전달할뿐임&lt;/li&gt;
&lt;li&gt;실데 디스크 입출력 작업(데이터 읽고 메모리로 전송)은 전용 하드웨어가 처리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;디스크가 하나의 트랙 탐색 입출력 요청을 완료하는데 소요되는 시간은 ms단위 수준
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우리 스레드에서 &lt;b&gt;입출력 과정이 실행되는 동안 cpu 제어권을 다른 스레드에 넘겨 작업 (블로킹)&lt;/b&gt;할수 있도록 함&lt;/li&gt;
&lt;li&gt;완료되면 cpu 제어권을 우리 스레드 혹은 프로세스에서 넘겨받아 다음 작업을 실행함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cf) 스레드는 I/O 요청을 기다리는 동안 블로킹 상태로 있게 되지만, 시스템은 다른 스레드가 CPU를 활용할 수 있도록 CPU 제어권을 넘기기 때문에 스레드 차원에서의 블로킹은 맞고, 시스템 차원에서 다른 스레드가 CPU를 사용할 수 있도록 한다는 점에서는 논블로킹처럼 보이기도 함. 하지만 개념적으로 스레드가 기다린다는 측면에서 &quot;블로킹&quot;이 맞음&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cpu제어권을 상실했다가 되찾는 시간 동안 스레드나 프로세스는 블로킹되어 일시 중지됨 (스레드 양도)&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;호출 스레드가 블로킹되지 않고 입출력 작업처리하는 방법 : 논블로킹&lt;/b&gt;&lt;/span&gt; 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;논블로킹과 비동기 입출력&lt;/b&gt;&lt;/span&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 수신 함수 recv가 &lt;b&gt;논블로킹&lt;/b&gt;이면 이 함수를 호출할 때 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;운영체제는 호출 스레드를 일시 중지시키는 대신 recv 함수 즉시 반환&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출 스레드는 자신의 작업을 계속하며 &lt;span style=&quot;color: #ef5369;&quot;&gt;데이터 수신 작업은 &lt;b&gt;커널이 진행&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;결과 확인 함수 제공&lt;/li&gt;
&lt;li&gt;알림 작동 방식&lt;/li&gt;
&lt;li&gt;수신처리 콜백함수를 recv인자로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;이런 유형의 입출력 작업이 &lt;b&gt;비동기 입출력&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;동기와 블로킹, 비동기와 논블로킹&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동기 호출이 항상 블로킹인 것은 아님&lt;/li&gt;
&lt;li&gt;&lt;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;funcA함수가 sum함수를 호출했다고 블로킹되거나 스레드가 일시중지 되지는 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;recv함수에 비동기 콜백함수 전달한 것은 &lt;b&gt;논블로킹이면서 비동기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;recv함수 호출 후 check함수로 계속해서 결과 감지한다면 &lt;b&gt;논블로킹이지만 동기&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;높은 동시성과 고성능을 갖춘 서버 구현&lt;/h4&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 먼저 출현한 기술&lt;/li&gt;
&lt;li&gt;간단한 형태의 병행처리방식 일종인 다중 프로세스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스간 통신에 난이도&lt;/li&gt;
&lt;li&gt;프로세스 생성 종료 성능 부담&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;&lt;b&gt;프로세스 주소 공간 공유&lt;/b&gt;하기 때문에 리소스 공유에 별도 통신 필요 없음&lt;/li&gt;
&lt;li&gt;스레드 생성 종료 부담 적음&lt;/li&gt;
&lt;li&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;주소공간 공유하기 때문에 하나의 스레드 강제 종료 시 &lt;b&gt;같은 프로세스 공유하는 모든 스레드와 프로세스 강제 종료&lt;/b&gt;됨&lt;/li&gt;
&lt;li&gt;여러 스레드가 동시에 공유 리소스 읽고 쓸 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다중 스레드 문제
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 안전 문제 고려 프로그래밍&lt;/li&gt;
&lt;li&gt;가볍긴 하나 초당 수십만 건 요청마다 스레드 생성 정도는 성능 이슈 발생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 소비&lt;/li&gt;
&lt;li&gt;스레드 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이벤트 순환&lt;/b&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;다중 프로세스, 다중 스레드에 이은 또다른 병렬처리의 방식&lt;/li&gt;
&lt;li&gt;이벤트 기반의 동시성을 이용한 &lt;b&gt;이벤트 기반 프로그래밍&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이벤트와 이벤트 처리함수&lt;/li&gt;
&lt;/ul&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;서버에서 다루는 이벤트는 대부분 입출력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 데이터 수신 여부, 파일의 읽기 및 쓰기 여부 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;일반적으로 이벤트 핸들러&lt;/li&gt;
&lt;/ul&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;서버에서 이벤트는 사용자 요청이며, 이벤트를 계속 수신하고 처리해야함&lt;/li&gt;
&lt;li&gt;반복문 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731017780864&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;while(true){
    event = getEvent();
    handler(event);
}&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;이벤트 순환 문제 두가지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문제1 - getEvent() 같은 함수 하나로 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;어떻게 이벤트를 가져올지??&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;입출력 다중화 기술&lt;/span&gt;로 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문제 2 - handler() 함수가 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;이벤트 순환과 동일한 스레드에서 실행되어야할지??&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;b&gt;문제1 해결 방법 &lt;/b&gt;- 이벤트 소스와 입출력 다중화&lt;/b&gt;&lt;/span&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓도 예외 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;예시 - 사용자 연결이 열개이고, 이에 대응하는 소켓 디스크립터가 열개 있는 서버가 데이터를 수신하려고 대기 중인 상횡
&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;첫번째 사용자가 데이터를 보내지 않는 한 recv(fd1, buf1)는 반환되지 않으므로 서버가 두번째 사용자의 데이터를 수신 및 처리 불가..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731017856328&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	recv(fd1, buf1);
	recv(fd2, buf2);
	recv(fd3, buf3);
	//..&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 style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;더 나은 작동 방식은 &lt;b&gt;운영체제에 &amp;lsquo;소켓 디스크립터 열개 감시하고 있다가 데이터가 들어오면 알려줘&amp;rsquo;&lt;/b&gt; 라는 내용 전달하는 작동 방식 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런 작동 방식을 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;입출력 다중화&lt;/b&gt;&lt;/span&gt;라함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입출력 다중화는 이벤트 순환 엔진이 된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리눅스에서 유명한 것이 epoll&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1731019037136&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이벤트 순환을 위한 epoll 생성
epoll_fd = epoll_create(); //getEvent 동일

// 서술자를 epoll이 처리하도록 지정
Epoll_ctl(epoll_fd, fd1, fd2, fd3, fd4...);

while (1)
{
    int n = epoll_wait(epoll_fd);

    for (i = 0; i &amp;lt; n; i++)
    {
        // 특정 이벤트 처리
    }
}&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;두번째 문제 : 이벤트 순환과 다중 이벤트&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li style=&quot;list-style-type: none;&quot;&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트 핸들러에서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입출력 작업이 없고&lt;/li&gt;
&lt;li&gt;처리시간이 짧을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이러한 상황은 이벤트 순환과 이벤트 핸들러가 같은 스레드에서 실행되도 문제 없음&lt;/li&gt;
&lt;li&gt;하지만 사용자 요청을 처리하는데 cpu가 많은 시간을 소모한다면??
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청A를 처리하는 중에 요청 B를 처리할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;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;문제2 해결 방법 - 다중 스레드&lt;/b&gt; (반응자 패턴)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;이벤트 순환과 이벤트 핸들러가 다른 스레드&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&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;span style=&quot;color: #ef5369;&quot;&gt;스레드 풀&lt;/span&gt;로 관리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 설계 방법은 &lt;b&gt;반응자 패턴&lt;/b&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;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청 처리 과정에서 입출력도 존재하는 경우에서 두가지 상황&lt;/li&gt;
&lt;li&gt;&lt;b&gt;1. 입출력 작업에 대응하는 논블로킹 인터페이스가 있는 경우&lt;/b&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2. 입출력 작업에 블로킹 인터페이스만 있는 경우 - &lt;span style=&quot;color: #ef5369;&quot;&gt;이벤트 순환 내부에서 블록킹 호출하면 안됨&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트 순환내에서 절대로 블로킹 인터페이스를 호출해선 안됨&lt;/li&gt;
&lt;li&gt;이벤트 순환 스레드가 일시중지 될 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;블로킹 입출력 호출이 포함된 작업은 작업자 스레드에 전달&lt;/b&gt;해야함!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&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;예시) 서버는 일반적으로 원격 프로시저 호출, 즉 RPC를 통해 통신함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RPC는 네트워크 설정, 데이터 전송, 데이터 분석 등 지루한 작업을 담아 프로그래머가 일반함수를 호출하는 것처럼 네트워크로 통신할 수 있도록 처리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;void handler(request) {
    A;
    B;
    GetUserInfo(request, response);  // 서버 A에 요청
    C;
    D;
    GetQueryInfo(request, response); // 서버 B에 요청
    E;
    F;
    GetStorkInfo(request, response); // 서버 C에 요청
    G;
    H;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Get으로 시작하는 RPC호출은 모두 블로킹 호출이기 때문에 사용자가 응답하기 전에는 함수가 반환되지 않음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블로킹 호출이기 때문에 스레드가 일시중지 될 수 있고 블로킹 호출이 여러번 발생하면 스레드 빈번하게 중단되어 cpu 비효율적임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더 나은 방식은 &lt;b&gt;동기 방식의 RPC 호출을 비동기 호출로 바꾸기&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처리할 내용을 콜백함수에 담아 RPC 호출하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;void handler_after_GetStorkInfo(response) {
    G;
    H;
}

void handler_after_GetQueryInfo(response) {
    E;
    F;
    GetStorkInfo(request, handler_after_GetStorkInfo); // 서버 C에 요청
}

void handler_after_GetUserInfo(response) {
    C;
    D;
    GetQueryInfo(request, handler_after_GetQueryInfo); // 서버 B에 요청
}

void handler(request) {
    A;
    B;
    GetUserInfo(request, handler_after_GetUserInfo);   // 서버 A에 요청
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주 프로세스는 네 개로 분할되었고 &lt;span style=&quot;color: #ef5369;&quot;&gt;콜백안에 콜백&lt;/span&gt;에 포함되게 됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 &lt;span style=&quot;color: #ef5369;&quot;&gt;서비스가 더 많아지면 이런 형태의 코드는 관리가 불가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;코루틴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;코루틴 : 동기 방식의 비동기 프로그래밍&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 언어나 프레임워크가 코루틴을 지원하면 아래와 같이 handler 함수가 코루틴에서 실행되도록 할 수 있음&amp;nbsp;&lt;/li&gt;
&lt;li class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;void handler() {
    A; //호출 진입점
    B;
    GetUserInfo(); //연결시작지점 반환
    C; //호출 진입점
    D;
    GetQueryInfo(); //연결시작지점 반환
    E; //호출 진입점
    F;
    GetStorkInfo(); //연결시작지점 반환
    G;
    H; //반환
}&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;handler 함수의 코드구현은 동기&lt;/li&gt;
&lt;li&gt;하지만 yield로 cpu 제어권을 반환하는 등 RPC 통신이 시작된 후 적극적으로 바로 호출된다는 점이 다르다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 RPC 호출함수나 네트워크 데이터 전송 같이 &lt;b&gt;기존 함수를 수정해야 yield로 cpu 제어권 반환&lt;/b&gt;할 수 있다는 점 유의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가장 중요한 점은 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;코루틴이 일시 중지 되더라도 작업자 스레드가 블로킹 되지 않는다&lt;/b&gt;&lt;/span&gt;는 점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코루틴과 스레드를 사용하는 블로킹 호출의 가장 큰 차이점&lt;/li&gt;
&lt;/ul&gt;
&lt;/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;코루틴이 일시중지되면&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;작업자 스레드는 준비 완료된 다른 코루틴을 실행하기 위해 전환&lt;/b&gt;&lt;/span&gt;되며&lt;/li&gt;
&lt;li&gt;일시 중지된 코루틴에 할당된 사용자 서비스가 응답한 후 그 처리결과를 반환하면&lt;/li&gt;
&lt;li&gt;다시 준비 상태가 되어 스케줄링 상태가 돌아오길 기다림&lt;/li&gt;
&lt;li&gt;이후 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;코루틴은 마지막으로 중지되었던 곳에서 이어서 계속 실행&lt;/b&gt;&lt;/span&gt;됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코루틴의 도움으로 &lt;b&gt;동기 방식 프로그래밍하더라도 비동기 실행과 같은 효과&lt;/b&gt;를 얻을 수 있게됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lsUW0/btsKAGJr2Hm/5iOv83vdOC33EPRh6u6vRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lsUW0/btsKAGJr2Hm/5iOv83vdOC33EPRh6u6vRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lsUW0/btsKAGJr2Hm/5iOv83vdOC33EPRh6u6vRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlsUW0%2FbtsKAGJr2Hm%2F5iOv83vdOC33EPRh6u6vRk%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;1398&quot; height=&quot;550&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;550&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;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;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;우리가 구현한 handler 함수를 코루틴에 담아 스케줄링과 실행을 위해 각 작업자 스레드에 배포함&lt;/li&gt;
&lt;li&gt;작업자 스레드는 코루틴을 획득한 후 진입 함수인 handler를 실행하기 시작&lt;/li&gt;
&lt;li&gt;어떤 코루틴이 능동적으로 cpu 제어권를 반환하면&lt;/li&gt;
&lt;li&gt;작업자 스레드는 준비 상태인 다른 코루틴을실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코루틴이 블로킹 방식으로 RPC를 호출하더라도 작업자 스레드는 블로킹되지 않기 때문&lt;/b&gt;에 시스템 리소스를 효율적으로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;cf) 스레드 vs 코루틴 비교&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;스레드 실행정보는 스택 영역&lt;/b&gt;&lt;/span&gt;에서 따로 관리 되어 다른 스레드가 해당 정보를 이용하기 어려움
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;운영체제가 자동으로 관리&lt;/b&gt;&lt;/span&gt;하고 스케줄링해주는 장점이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;코루틴 실행정보는 힙 영역에서 공유&lt;/b&gt;&lt;/span&gt;될 수 있으므로 가능하여 다른 스레드가 이어서 작업 용이
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코루틴은 프레임워크가 관리해주는 측면이 있지만, 비동기 처리와 관련된 설계 및 제어를 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;개발자가 신경 써야 하는 부분이 있어 작성시 고려&lt;/b&gt;&lt;/span&gt;해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU, 스레드, 코루틴 관계&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyn7c3/btsKBJyGMFh/GkrBSiSpFY6igeF11mvm01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyn7c3/btsKBJyGMFh/GkrBSiSpFY6igeF11mvm01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyn7c3/btsKBJyGMFh/GkrBSiSpFY6igeF11mvm01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyn7c3%2FbtsKBJyGMFh%2FGkrBSiSpFY6igeF11mvm01%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;1392&quot; height=&quot;832&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;832&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기계명령어를 실행하여 컴퓨터를 움직이게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;커널 상태 스레드&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;커널로 생성되고 스케줄링을 함&lt;/li&gt;
&lt;li&gt;커널은 스레드 우선순위에 따라 CPU 연산 리소스를 할당함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코루틴
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;커널 입장에서 코루틴을 모름&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;코루틴이 얼마나 많이 생성되었든 &lt;b&gt;커널은 이와 관계 없이 스레드에 따라 CPU 시간을 할당&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;스레드에 할당된 CPU시간을 &lt;b&gt;사용자 상태에서 재차 할당&lt;/b&gt;하는 것&lt;/li&gt;
&lt;li&gt;사용자 상태에서 할당되므로 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;코루틴을 사용자 상태 스레드&lt;/b&gt;&lt;/span&gt;라고도 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;1118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZt0Ov/btsKAeT7RYF/iZ2SYK1zKXdCPgIlKLjsQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZt0Ov/btsKAeT7RYF/iZ2SYK1zKXdCPgIlKLjsQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZt0Ov/btsKAeT7RYF/iZ2SYK1zKXdCPgIlKLjsQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZt0Ov%2FbtsKAeT7RYF%2FiZ2SYK1zKXdCPgIlKLjsQk%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;1374&quot; height=&quot;1118&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;1118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>컴퓨터 과학/[책] 컴퓨터 밑바닥의 비밀</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/211</guid>
      <comments>https://goalinnext.tistory.com/211#entry211comment</comments>
      <pubDate>Fri, 8 Nov 2024 06:42:07 +0900</pubDate>
    </item>
    <item>
      <title>학습일지 241027</title>
      <link>https://goalinnext.tistory.com/209</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yml 파일 생성 중에 모델을 아직 만들지 않았을 경우 에러 발생&lt;br /&gt;-&amp;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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;url -&amp;gt; subscription -&amp;gt; subscriptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;subscriptions 하나만으로 '사용자 구독한 블로그 목록을 요청'
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/blogs/subscriptions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;구독 해지 요청 -&amp;gt; 사용자가 특정 블로그 구독 해지 요청&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;url에 버전을 넣는게 좋을까 &lt;b&gt;고민&lt;/b&gt;&lt;/span&gt; -&amp;gt; 상위호환성 하위호환성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;url로 호환성을 챙기는게 좋은 것 같지 않는 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;api가 많으면 복잡성이 많아짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로토콜 버퍼 상위호환성 하위호환성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&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;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;블로그 상태메시지 생성에서 id인자는 사용자 아이디로 필요가 없을 것&lt;/li&gt;
&lt;li&gt;블로그 인증 -&amp;gt; body에 url 으로 요청&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;상태 코드 관한 고민&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;201 코드 같은 것은 쓰면 좋음&lt;/li&gt;
&lt;li&gt;404&lt;/li&gt;
&lt;li&gt;500 에러 같은 것은 당연한데 작성 안하는 경우가 많다 -&amp;gt; 작성은 하는 게 좋다.&lt;/li&gt;
&lt;li&gt;API 먼저 작성할 때 풍부하게 정도는 아니고,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 요구사항에 맞는 status를 탐구한다 정도(좀 더 다른 케이스가 없지 않을까 고민.. )&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;blog -&amp;gt; 나의 블로그 라는 의미를 담는 단어 .. -&amp;gt; my&lt;/li&gt;
&lt;/ul&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;상태메시지 생성 -&amp;gt; put 일 확률이 높지 않을까&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;시멘틱 버저닝&lt;/span&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://semver.org/lang/ko/&quot;&gt;https://semver.org/lang/ko/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1729989170477&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;유의적 버전 2.0.0&quot; data-og-description=&quot;Semantic Versioning spec and website&quot; data-og-host=&quot;semver.org&quot; data-og-source-url=&quot;https://semver.org/lang/ko/&quot; data-og-url=&quot;https://semver.org/lang/ko/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://semver.org/lang/ko/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://semver.org/lang/ko/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;유의적 버전 2.0.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Semantic Versioning spec and website&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;semver.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 응답 필드 추가 해보기&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해볼 수 있는 것들&lt;/li&gt;
&lt;li&gt;화면 구상&lt;/li&gt;
&lt;li&gt;유즈케이스 - 엑셀 드로우 등.&lt;/li&gt;
&lt;/ul&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랭킹, 현황 -&amp;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;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;선택한 블로그 방문됨 -&amp;gt; 특정 블로그가 클릭됨&lt;/li&gt;
&lt;li&gt;신청한 구독 내역 확인됨 -&amp;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;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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트 영어로 바꿔보기**&lt;/li&gt;
&lt;li&gt;화면 구상해보기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하다보면 API 속성 추가될수도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;유즈케이스-시나리오 작성 -&amp;gt; 러프하게&lt;/li&gt;
&lt;li&gt;클래스 다이어그램에
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연관 관계 표현하기&lt;/li&gt;
&lt;li&gt;일단은 도메인 모델링을 코드로 변환하는 중간과정이라고 생각.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;도메인 -&amp;gt; 애플리케이션 -&amp;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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기술은 추후에&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/209</guid>
      <comments>https://goalinnext.tistory.com/209#entry209comment</comments>
      <pubDate>Sun, 27 Oct 2024 09:32:02 +0900</pubDate>
    </item>
    <item>
      <title>컴퓨터 맨 밑바닥의 비밀 - 2. 운영체제, 프로세스, 스레드, 코루틴</title>
      <link>https://goalinnext.tistory.com/208</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;개요&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영체제 목적&lt;/li&gt;
&lt;li&gt;운영체제 없이 프로그램 실행은 가능&lt;/li&gt;
&lt;li&gt;자동 적재 및 멀티 태스킹 지원&lt;/li&gt;
&lt;li&gt;프로세스 목적&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;가용 cpu를 최대한 이용하고자 프로세스 진입함수를 추가로 생성하여 스레드 활용&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CPU&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cpu는 메모리에서 명령어를 가져오고 명령어를 실행하는 작업만 할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;운영체제&lt;/h4&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;운영체제가 없어도 cpu가 프로그램을 실행하도록 할 수 있지만 적절한 메모리 영역 찾기, pc 레지스터 설정 등등 매우 불편하다&lt;/li&gt;
&lt;li&gt;내가 지금 사용하는 프로그램이 &lt;b&gt;cpu와 표준 크기 메모리를 독점하고 있다고 생각할 수 있게 해줌&lt;/b&gt; -&amp;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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로세스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cpu가 어떤 기계 명령어를 실행했는지와 cpu내부 기타 레지스터 값 등 &lt;b&gt;상태값을 저장해둔 구조체를 프로세스&lt;/b&gt;라고 한다.&lt;/li&gt;
&lt;li&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;운영체제의 가상 메모리는 각 프로세스가 표준적인 메모리 크기를 독점적으로 사용하는 것처럼 보이게 함&lt;/li&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;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;/li&gt;
&lt;li&gt;다중 프로세스 프로그래밍 단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) afunc이 bfunc의 결과를 필요로 할 때&lt;/li&gt;
&lt;li&gt;프로세스 생성 시 오버헤드&lt;/li&gt;
&lt;li&gt;프로세스 마다의 자제척 주소 공간으로 프로세스 간 통신은 더 복잡함&lt;/li&gt;
&lt;li&gt;프로세스 단점&lt;/li&gt;
&lt;li&gt;진입 함수가 main 하나 밖에 없어서 프로세스의 기계명령어를 한 번에 하나의 cpu에서만 실행 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cpu 여러대가 동일한 프로세스의 기계명령어를 실행하게 할 방법&lt;/b&gt;은??&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pc레지스터가 진입 함수로 main을 지정한 것과 동일한 방식으로 &lt;b&gt;pc레지스터가 다른 함수를 지정하고 이를 통해 새로운 실행 흐름을 형성&lt;/b&gt;할 수 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-&amp;gt; 스레드&lt;/li&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스레드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pc레지스터를 통해 하나의 프로세스에 &lt;b&gt;두개 이상의 진입 함수를 설정하여 cpu 여러개가 동시에 프로세스의 기계 명령어를 실행&lt;/b&gt;할 수 있다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;== 공유 프로세스 주소 공간에서 동일한 프로세스에 속한 기계 명령어를 동시에 실행 가능&lt;/li&gt;
&lt;li&gt;== 하나의 프로세스 안에 여러 개의 실행 흐름 존재 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하나의 프로세스 내에서 이루어 지므로 프로세스 간 통신이 필요없음. 프로세스 내의 스레드들은 변수들을 공유 가능. 자신이 속해 있는 프로세스 주소 공간을 공유한다는 의미
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 덕분에 프로세스를 시작하고 스레드를 여러개 생성하여 모든 cpu를 최대한 사용하여 다중 코어를 충분히 이용할 수 있게 된다. -&amp;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다중 스레드&lt;/h4&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; cpu가 기계명령어를 실행할 때도 어느 스레드에 속하는지 인식 못함
&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;cpu가 명령어를 실행할 때 스레드 고려 않기 때문에 공유 리소스 접근 시 버그 발생 가능성이 있음&lt;/li&gt;
&lt;li&gt;상호배제와 동기화 이용하여 개발자가 명시적으로 해결해야함&lt;/li&gt;
&lt;/ul&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;cpu와 스레드는 진입함수 주소로 관련을 갖는것을 위에서 봄 -&amp;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;함수 실행 시 필요정보&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;b&gt;각 스레드는 프로세스 주소 공간에 자신만을 위한 스택영역을 갖고 있으며 이를 인지하고 있음&lt;/b&gt;&lt;/li&gt;
&lt;li&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;워드 문서 편집&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;짧은 작업
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 서버, 디비 서버, 파일 서버, 디비 서버 등&lt;/li&gt;
&lt;li&gt;두가지 특징&lt;/li&gt;
&lt;li&gt;작업 처리 필요 시간 짧음&lt;/li&gt;
&lt;li&gt;작업 수가 매우 많음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;서버가 하나의 요청을 받으면 해당 작업 처리하는 스레드 생성, 처리 완료되면 스레드 종료&lt;/li&gt;
&lt;li&gt;짧은 작업엔 부적합&lt;/li&gt;
&lt;li&gt;스레드 생성 종료 오버헤드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스레드마다 독립적인 스택영역이 필요하므로&lt;/b&gt; 메모리와 기타 시스템 리소스 많이 소비&lt;/li&gt;
&lt;li&gt;스레드 전환 오버헤드&lt;/li&gt;
&lt;li&gt;요청 당 스레드 방식 단점 해결 위해 &lt;b&gt;스레드 풀&lt;/b&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스레드풀&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 재사용이 핵심&lt;/li&gt;
&lt;li&gt;스레드 여러개를 미리 생성해두고 작업 생기면 해당 스레드에게 요청&lt;/li&gt;
&lt;li&gt;스레드 생성 및 종료가 빈번하지 않으며 스레드 개수 관리되므로 메모리 안정적&lt;/li&gt;
&lt;li&gt;스레드풀 내에 있는 스레드에게 작업 전달하기&lt;/li&gt;
&lt;li&gt;큐 자료구조 (생산자 소비자 패턴)&lt;/li&gt;
&lt;li&gt;스레드풀에 전달되는 작업은 구조체로서 데이터와 함수 부분으로 구성됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생산자가 작업 대기열에 데이터를 기록하면 스레드가 깨어나고 작업 구조체를 가져와 구조체의 처리함수를 실행&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;작업 대기열에서 동기화 시 상호문제 처리 필요&lt;/li&gt;
&lt;li&gt;스레드풀에서 적당한 스레드 개수&lt;/li&gt;
&lt;li&gt;cpu 집약 작업 vs i/o 집약 작업&lt;/li&gt;
&lt;li&gt;cpu 집약 작업은 외부 입출력 필요 없으므로 스레드 수와 cpu코어 수 같다면 cpu리소스를 충분히 활용 가능&lt;/li&gt;
&lt;li&gt;i/o 집약작업은 성능 테스트 도구로 입출력 대기시간과 cpu연산 시간 평가 필요 -&amp;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;프로세스와 스레드&lt;/li&gt;
&lt;li&gt;운영체제가 제공하는 두가지 추상화 개념&lt;/li&gt;
&lt;li&gt;프로세스는 운영체제가 리소스를 할당하는 기본 단위&lt;/li&gt;
&lt;li&gt;스레드는 스케줄링 기본단위, &lt;b&gt;프로세스 리소스는 스레드간 공유&lt;/b&gt;됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스레드 개별 전용 리소스 (스택영역)&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 실행에는 항상 하나의 시작점 존재&lt;/li&gt;
&lt;li&gt;이 시작점이 &lt;b&gt;진입함수&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;cpu는 진입 함수에서 실행 시작하여 실행 흐름 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이 진입 함수에서 시작된 실행흐름이 스레드&lt;/b&gt;&lt;/li&gt;
&lt;li&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;반환값&lt;/li&gt;
&lt;li&gt;매개변수&lt;/li&gt;
&lt;li&gt;지역변수&lt;/li&gt;
&lt;li&gt;레지스터 정보&lt;/li&gt;
&lt;li&gt;pc 레지스터&lt;/li&gt;
&lt;li&gt;스택 포인터 등&lt;/li&gt;
&lt;li&gt;스레드 여러개 있을 때 각 스레드가 독점하는 스택 영역들이 존재하게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스레드 상황정보(컨텍스트)&lt;/li&gt;
&lt;li&gt;스레드는 프로세스 주소 공간에서 위 스택영역을 제외한 나머지 영역 공유함&lt;/li&gt;
&lt;li&gt;스레드 공유 리소스 vs 전용 리소스&lt;/li&gt;
&lt;li&gt;위의 스택 영역을 제외하고 스레드 간 리소스를 공유한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 영역, 데이터 영역, 힙 영역&lt;/li&gt;
&lt;li&gt;스택 영역은 스레드 별 전용 리소스 공간&lt;/li&gt;
&lt;li&gt;코드영역 : 모든 함수는 스레드에 배치될 수 있음&lt;/li&gt;
&lt;li&gt;프로세스 주소 공간 중 &lt;b&gt;코드영역에는 코드, 즉 컴파일한 후 생성된 실행가능한 기계명령어가 저장됨&lt;/b&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;b&gt;특정 함수를 특정 스레드에서만 실행되도록 하는 것은 불가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;읽기전용&lt;/li&gt;
&lt;li&gt;어떤 스레드도 코드 영역의 내용을 변경할 수 없음&lt;/li&gt;
&lt;li&gt;따라서 스레드 안전 문제가 발생하지 않음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역 변수가 저장되는 곳&lt;/li&gt;
&lt;li&gt;프로세스 주소 공간 중 데이터 영역에 저장됨&lt;/li&gt;
&lt;li&gt;전역 변수에 &lt;b&gt;모든 스레드가 접근 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;프로그램이 실행되는 동안 데이터 영역 내의 &lt;b&gt;전역 변수의 인스턴스는 하나만&lt;/b&gt; 있기 때문&lt;/li&gt;
&lt;/ul&gt;
힙영역 : 포인터가 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;malloc 함수나 new로 요청되는 메모리가 이 영역에 할당됨&lt;/li&gt;
&lt;li&gt;모든 스레드는 해당 변수 주소, 즉 포인터를 얻을 수 있다면 데이터에 접근 가능&lt;/li&gt;
&lt;li&gt;따라서 힙 영역은 스레드간 공유 리소스가 되는 것임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 영역: 모든 스레드가 접근 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택영역 : 공유 공간 내 전용 데이터&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드의 추상화 측면에서 바라보면 스택 영역은 스레드 전용 공간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하지만 실제 구현 측면에서 엄밀하게 격리된 스레드 전용 공간은 아니다&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;서로 다른 프로세스의 주소 공간은 서로 격리되어 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가상 메모리 시스템&lt;/b&gt;은 특수힌 경우 제외하고 &lt;b&gt;다른 프로세스의 주소 공간에 접근 하지 못하도록 보장&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;하지만 &lt;b&gt;하나의 프로세스 내에서는 접근 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드 간 스택 영역에 접근 하지 못하도록 하는 보장은 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다른 스레드의 스택 프레임의 포인터를 가져올 수 있다면 데이터에 접근 및 수정 가능&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;버그로 이어질 가능성 존재&lt;/li&gt;
&lt;li&gt;다른 스레드의 전용 데이터를 수정하는 경우라면 버그의 원인을 찾기 굉장히 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;동적 링크 라이브러리와 파일&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링크는 컴파일 후 최종적으로 실행 파일을 생성하는 핵심적인 단계&lt;/li&gt;
&lt;li&gt;정적링크와 동적링크로 구분&lt;/li&gt;
&lt;li&gt;정적링크는 종속된 모든 라이브러리가 실행 파일에 포함되는 것을 의미&lt;/li&gt;
&lt;li&gt;동적링크는 포함되어 있지 않아 시작 또는 실행 중에 코드와 데이터를 찾아 프로세스 주소 공간에 넣는 링크 과정이 필요&lt;/li&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;li&gt;모든 스레드가 공유 가능&lt;/li&gt;
&lt;li&gt;프로그램이 동작 중에 &lt;b&gt;특정 파일을 열면 프로세스 주소 공간에 열린 파일 정보도 저장됨&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드간 공유 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스레드 전용 저장소(thread local storage)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 영역에 저장된 변수는 모든 스레드에서 접근 가능&lt;/li&gt;
&lt;li&gt;하지만 사실 &lt;b&gt;변수의 인스턴스는 각각의 스레드에 속함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;하나의 스레드에서 값을 변경해도 다른 스레드에서 적용되지 않음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__thread int a = 1;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;컴파일러가 전역변수 a를 스레드 전용 저장소에 넣도록 지시하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;스레드 안전&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 안전을 이해하지 못하면 다중 스레드 프로그래밍을 다루기 어려움&lt;/li&gt;
&lt;li&gt;전용 리소스만 사용하는 스레드는 스레드 안전을 달성할 수 있음&lt;/li&gt;
&lt;li&gt;공유 리소스 사용하는 스레드는 &lt;b&gt;대기 제약 조건에 맞게 리소스 사용&lt;/b&gt;하면 스레드 안전 달성&lt;/li&gt;
&lt;li&gt;스레드 안전이란?&lt;/li&gt;
&lt;li&gt;어떤 코드가 주어졌을 때 그 코드가 &lt;b&gt;스레드 몇 개에서 호출되든 어떤 순서로 호출되든 올바른 결과가 나온다면 스레드 안전&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드 안전 코드 작성하는 방법?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;어떤 것이 스레드 전용 리소스고 어떤 것이 공유 리소스인지 구분&lt;/b&gt;할 수 있어야함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스레드 전용 리소스와 공유 리소스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 전용 리소스&lt;/li&gt;
&lt;li&gt;함수의 지역 변수&lt;/li&gt;
&lt;li&gt;스레드의 스택 영역&lt;/li&gt;
&lt;li&gt;스레드 전용 저장소&lt;/li&gt;
&lt;li&gt;스레드 공유 리소스 : &lt;b&gt;순서&lt;/b&gt;가 중요&lt;/li&gt;
&lt;li&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;&lt;/li&gt;
&lt;li&gt;코드 영역&lt;/li&gt;
&lt;li&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;&lt;b&gt;순서의 핵심은 공유 리소스를 사용하는 작업이 다른 스레드를 방해할 수 없다는 것&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이를 위해 락이나 세마포어 같은 장치를 사용하여 공유 리소스 순서를 지킬 수 있음&lt;/li&gt;
&lt;/ul&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;스레드 전용 리소스인 지역 변수만 사용하는 함수를 무상태 함수라 함&lt;/li&gt;
&lt;li&gt;당연하게 스레드 안전&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;li&gt;값으로 전달된 매개변수는 스택영역에 있기 때문&lt;/li&gt;
&lt;li&gt;&lt;b&gt;매개변수를 포인터로 전달하는 경우&lt;/b&gt;에는 스레드 안전하지 않을 수 있음&lt;/li&gt;
&lt;li&gt;포인터로 전달된 변수가 지역 변수라면 스레드 안전&lt;/li&gt;
&lt;li&gt;그 외는 락과 같은 순서가 반드시 부여되어야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;개선방법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드간 공유 리소스 사용하지 못하도록 함수 호출 시 해당 스레드에 속하는 리소스 주소를 전달하기&lt;/li&gt;
&lt;li&gt;스레드가 공유 리소스 사용해야하는 상황이라면??&lt;/li&gt;
&lt;li&gt;&lt;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;전역 변수를 초기화 후 읽기 전용으로만 사용한다면 공유 리소스 사용하면서 스레드 안전&lt;/li&gt;
&lt;li&gt;수정이 필요한 경우 스레드 전용 저장소 변수로 선언하면 스레드 안전&lt;/li&gt;
&lt;/ul&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;값를 반환하는 경우&lt;/li&gt;
&lt;li&gt;스레드 안전&lt;/li&gt;
&lt;li&gt;포인터를 반환하는 경우&lt;/li&gt;
&lt;li&gt;스레드 안전하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;funcA가 스레드 안전이 아닌 함수를 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;funcA도 기본적으로 스레드 안전하지 않지만&lt;/li&gt;
&lt;li&gt;스레드 안전하지 않은 함수 호출 전후에 락으로 보호하면 funcA는 스레드 안전&lt;/li&gt;
&lt;/ul&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;스레드 간 어떤 리소스 공유하는지 고려하기&lt;/li&gt;
&lt;li&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;으로 사용&lt;/li&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;li&gt;&lt;b&gt;순서&lt;/b&gt; 프로그래머가 유지&lt;/li&gt;
&lt;li&gt;뮤텍스, 스핀락, 세마포어 등을 활용하서 순서 보장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커널 스레드 vs 코루틴&lt;/b&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;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;&lt;b&gt;코루틴&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드보다 더 가벼운 실행 흐름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코루틴&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반함수는 return 명령어를 만나거나 코드의 마지막 줄까지 실행되어야 반환이 가능&lt;/b&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;이 존재
&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;b&gt;일시중지된 지점에서 이어서 실행&lt;/b&gt;됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반환된 후에도 함수 실행시의 정보 저장 필요&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;다시 실행될 때 해당 정보가 필요함&lt;/li&gt;
&lt;li&gt;코루틴은 자신이 마지막으로 실행된 위치를 알 수 있다는 점에서 일반 함수와 다르다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;스레드 안전이란?&lt;/li&gt;
&lt;li&gt;어떤 코드가 주어졌을 때 그 코드가 &lt;b&gt;스레드 몇 개에서 호출되든 어떤 순서로 호출되든 올바른 결과가 나온다면 스레드 안전&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드 안전 코드 작성하는 방법?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;어떤 것이 스레드 전용 리소스고 어떤 것이 공유 리소스인지 구분&lt;/b&gt;할 수 있어야함&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;운영체제 덕분에 개발자는 스레드 일시중지나 재시작을 명시할 필요가 없음&lt;/li&gt;
&lt;li&gt;진입 함수가 존재하면 운영체제가 자동적으로 여유 cpu를 가지고 스레드를 할당
&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코루틴 vs 일반함수&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 함수는 연결 시작 지점이 존재하지 않는 코루틴에 불과하다&lt;/li&gt;
&lt;/ul&gt;
&lt;/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;스레드 구현과 동일한 구조&lt;/li&gt;
&lt;li&gt;일시 중지되고 다시 시작할 수 있어야함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일시중지 될 때의 상태정보&lt;/b&gt;를 반드시 기록해야함&lt;/li&gt;
&lt;li&gt;런타임 스택프레임 정보&lt;/li&gt;
&lt;li&gt;cpu 레지스터 정보&lt;/li&gt;
&lt;li&gt;함수 실행시 상태 정보&lt;/li&gt;
&lt;li&gt;스레드는 이를 프로세스 주소 공간의 스택영역에 저장함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코루틴은 런타임 스택 프레임 정보를 힙영역에 저장&lt;/b&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;개발자가 동기 방식으로 비동기 프로그래밍을가능하게 함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>컴퓨터 과학/[책] 컴퓨터 밑바닥의 비밀</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/208</guid>
      <comments>https://goalinnext.tistory.com/208#entry208comment</comments>
      <pubDate>Fri, 25 Oct 2024 06:37:31 +0900</pubDate>
    </item>
    <item>
      <title>학습일지 241020</title>
      <link>https://goalinnext.tistory.com/207</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 논의&lt;br /&gt;-&amp;nbsp;&lt;a href=&quot;https://hyeon9mak.github.io/why-occurs-deadlock-from-select-query/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hyeon9mak.github.io/why-occurs-deadlock-from-select-query/&lt;/a&gt;&lt;br /&gt;- &lt;a href=&quot;https://happy-coding-day.tistory.com/299&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://happy-coding-day.tistory.com/299&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://happy-coding-day.tistory.com/299&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://happy-coding-day.tistory.com/299&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;# 레이어드 아키텍처 vs 헥사고날 아키텍처 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레이어드 아키텍처에서 무리한 DIP를 추구하려고 하면&lt;br /&gt;-&amp;nbsp;퍼시스턴스&amp;nbsp;레이어의&amp;nbsp;메서드를&amp;nbsp;추가할&amp;nbsp;때도&lt;br /&gt;-&amp;nbsp;비스니스&amp;nbsp;레이어&amp;nbsp;먼저&amp;nbsp;봐야하는&amp;nbsp;복잡함이&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;헥사고날-&amp;gt;&amp;nbsp;포트&amp;nbsp;인&amp;nbsp;아웃이&amp;nbsp;많음&lt;br /&gt;-&amp;nbsp;기능을&amp;nbsp;찾을&amp;nbsp;때&amp;nbsp;포트먼저&amp;nbsp;봄&lt;br /&gt;-&amp;nbsp;퍼시턴스&amp;nbsp;어댑터를&amp;nbsp;먼저&amp;nbsp;보지&amp;nbsp;않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;&amp;nbsp;무신사&amp;nbsp;집중&amp;nbsp;채용&amp;nbsp;중&amp;nbsp;&amp;nbsp;-&amp;gt;&amp;nbsp;써보기&amp;nbsp;..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;-&amp;nbsp;반버논&amp;nbsp;도메인주도&amp;nbsp;설계&amp;nbsp;핵심-&amp;gt;&amp;nbsp;얇은&amp;nbsp;책&amp;nbsp;추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;도메인&amp;nbsp;vs&amp;nbsp;성능&amp;nbsp;고민&amp;nbsp;&lt;br /&gt;-&amp;nbsp;성능은&amp;nbsp;인덱스&amp;nbsp;튜닝&amp;nbsp;정도로&amp;nbsp;&lt;br /&gt;-&amp;nbsp;경험&amp;nbsp;위주&amp;nbsp;(도메인&amp;nbsp;+&amp;nbsp;구조)&lt;br /&gt;-&amp;nbsp;서비스를&amp;nbsp;어떻게&amp;nbsp;풀어서&amp;nbsp;돈을&amp;nbsp;벌게&amp;nbsp;했는지..&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;-&amp;nbsp;사이드&amp;nbsp;프로젝트&amp;nbsp;만들어서&amp;nbsp;운영중에&amp;nbsp;있다면&amp;nbsp;많은&amp;nbsp;가산점이&amp;nbsp;있을&amp;nbsp;것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;-&amp;nbsp;헥사고날&amp;nbsp;이벤트&amp;nbsp;질문&amp;nbsp;&lt;br /&gt;-&amp;nbsp;관리하기가&amp;nbsp;어려움??&lt;br /&gt;- 글로벌 이벤트 &amp;amp; 로컬 이벤트&amp;nbsp;&lt;br /&gt;-&amp;nbsp;둘다&amp;nbsp;관리하기&amp;nbsp;어려움&lt;br /&gt;-&amp;nbsp;어디서&amp;nbsp;리스닝을&amp;nbsp;하고&amp;nbsp;있는지&amp;nbsp;모름&lt;br /&gt;-&amp;nbsp;트랜잭션&amp;nbsp;문제도&lt;br /&gt;-&amp;nbsp;유지보수&amp;nbsp;관점은&amp;nbsp;좋을&amp;nbsp;수&amp;nbsp;있어도&amp;nbsp;구현이&amp;nbsp;어려울&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;코드&amp;nbsp;위에&amp;nbsp;어디서&amp;nbsp;리스닝&amp;nbsp;하고&amp;nbsp;있음을&amp;nbsp;주석으로&amp;nbsp;남긴&amp;nbsp;적이&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;메시지&amp;nbsp;큐를&amp;nbsp;위한&amp;nbsp;사전을&amp;nbsp;만들기도&amp;nbsp;했음&amp;nbsp;-&amp;gt;&amp;nbsp;맵&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;이벤트&amp;nbsp;처리할&amp;nbsp;경우&amp;nbsp;장점&amp;nbsp;-&amp;nbsp;유지보수가&amp;nbsp;API보단&amp;nbsp;용이&lt;br /&gt;-&amp;nbsp;느슨한&amp;nbsp;결합&amp;nbsp;&lt;br /&gt;-&amp;nbsp;받는&amp;nbsp;쪽에서는&amp;nbsp;받는&amp;nbsp;것만&lt;br /&gt;-&amp;nbsp;보내는&amp;nbsp;것에서&amp;nbsp;보내는&amp;nbsp;것만&lt;br /&gt;-&amp;nbsp;응답값이&amp;nbsp;필요&amp;nbsp;없는&amp;nbsp;경우&amp;nbsp;-&amp;gt;&amp;nbsp;푸시&amp;nbsp;같은&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;API는&amp;nbsp;응답값이&amp;nbsp;올떄까지&amp;nbsp;기다려야함&lt;br /&gt;-&amp;nbsp;제&amp;nbsp;3의&amp;nbsp;방법으로&amp;nbsp;프로토콜&amp;nbsp;버퍼를&amp;nbsp;많이&amp;nbsp;쓰기도&amp;nbsp;함&amp;nbsp;(구글에서&amp;nbsp;만듬)&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;메시지큐&amp;nbsp;&lt;br /&gt;-&amp;nbsp;데이터&amp;nbsp;정합성의&amp;nbsp;단점&amp;nbsp;&lt;br /&gt;-&amp;nbsp;EDA&amp;nbsp;-&amp;nbsp;트랜잭션&amp;nbsp;아웃박스&amp;nbsp;패턴&lt;br /&gt;-&amp;nbsp;보상&amp;nbsp;트랜잭선&amp;nbsp;-&amp;gt;&amp;nbsp;보상이&amp;nbsp;안되는&amp;nbsp;이벤트도&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;- **API 문서 작성 &amp;amp; 애그리게이트**&lt;br /&gt;-&amp;nbsp;애그리게이트&lt;br /&gt;-&amp;nbsp;메시지를&amp;nbsp;중요시&amp;nbsp;생각하기&lt;br /&gt;-&amp;nbsp;객체의&amp;nbsp;책임과&amp;nbsp;메시지&amp;nbsp;&lt;br /&gt;-&amp;nbsp;필드를&amp;nbsp;일단&amp;nbsp;생각하지&amp;nbsp;말기&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>학습일지/학습일지</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/207</guid>
      <comments>https://goalinnext.tistory.com/207#entry207comment</comments>
      <pubDate>Sun, 20 Oct 2024 09:33:22 +0900</pubDate>
    </item>
    <item>
      <title>자바/스프링 개발자를 위한 실용주의 프로그래밍 7장 - 서비스</title>
      <link>https://goalinnext.tistory.com/206</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvr4Nx/btsJWuRhAHX/y44kbq6bQKLGV4air8LyO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvr4Nx/btsJWuRhAHX/y44kbq6bQKLGV4air8LyO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvr4Nx/btsJWuRhAHX/y44kbq6bQKLGV4air8LyO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvr4Nx%2FbtsJWuRhAHX%2Fy44kbq6bQKLGV4air8LyO1%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;2370&quot; height=&quot;1204&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7장 - 서비스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 서비스가 무엇인지?&lt;/li&gt;
&lt;li&gt;스프링에서 서비스는 왜 서비스인지?&lt;/li&gt;
&lt;li&gt;서비스 컴포넌트에 추가적으로 기대하는 역할이나 책임은 무엇인지?&lt;/li&gt;
&lt;/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;서비스의 컴포넌트의 역할 크게 3가지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 도메인 객체를 불러오기&lt;/li&gt;
&lt;li&gt;2 도메인 객체나 도메인 서비스에 일을 위임하기&lt;/li&gt;
&lt;li&gt;3 도메인 객체의 변경 사항을 저장하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스프링에서의 Service&lt;/h2&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;1390&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5C9Xp/btsJXWZ24Tt/igBU5S7F42PIFcQenKrvV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5C9Xp/btsJXWZ24Tt/igBU5S7F42PIFcQenKrvV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5C9Xp/btsJXWZ24Tt/igBU5S7F42PIFcQenKrvV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5C9Xp%2FbtsJXWZ24Tt%2FigBU5S7F42PIFcQenKrvV0%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;477&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@Service의 자바독 요약&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Service는 에릭 에반스의 DDD에서 영감을 받아 만들어진 애너테이션&lt;/li&gt;
&lt;li&gt;서비스는 J2EE 패턴 중 하나인 비즈니스 서비스 파사드처럼 사용될 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DDD&lt;/b&gt; (도메인 주도 설계)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DDD에서 정의하는 '서비스'란? -&amp;gt; &lt;b&gt;연산&lt;/b&gt;을 위한 컴포넌트&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DDD에서의 서비스 컴포넌트 (에릭에반스가 한 말)
&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;b&gt;활동이나 행동&lt;/b&gt;인데&lt;/li&gt;
&lt;li&gt;우리의 모델링 패러다임이 객체이므로 그러한 &lt;b&gt;연산도 객체와 잘 어울리게끔 노력&lt;/b&gt;해야함.&lt;/li&gt;
&lt;li&gt;이것이 스프링 서비스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서비스는 도메인 객체가 처리하기 애매한 '연산'자체를 표현하기 위한 컴포넌트&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로직을 도메인 세상 속 객체에 녹이기 어려운 상황?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 상품(Product), 쿠폰(Coupon), 사용자(User)의 마일리지(Mileage)라는 도메인이 있고&lt;/li&gt;
&lt;li&gt;가격 = 상품가격 - (상품가격x쿠폰최대할인율)-사용자마일리지&lt;/li&gt;
&lt;li&gt;서비스에 있는 비즈니스 로직을 도메인 객체가 처리하게 적용해보기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;//예시 1
user.calculatePrice(cupons, product); 
//예시 2
coupon.calculatePrice(user, product);  
//예시 3
product.calculatePrice(user, coupons);&lt;/code&gt;&lt;/pre&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 로직을 능동적인 객체에 표현하는 것 자체가 어렵기 때문&lt;/li&gt;
&lt;li&gt;가격 계산 로직 같은 것은 '&lt;b&gt;연산&lt;/b&gt;'-&amp;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;/h4&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Service  
@RequiredArgsConstructor  
public class ProductService{  
    PriceManager priceManger = new PriceManager(); //서비스가 서비스(매니저)를 호출하는 상황  
    priceManager.calculate(user, product, coupons);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Manager 클래스가 곧 서비스.
&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;이같은 행위자는 끝에 Manager와 같은 것이 붙는다 (에릭 에반스)&lt;/li&gt;
&lt;li&gt;...Manager가 나오면 접두어에 있는 모델은 관리하는 클래스..&lt;/li&gt;
&lt;li&gt;마찬가지로 &lt;b&gt;서비스&lt;/b&gt;도 어떤 모델과 강력하게 &lt;b&gt;연관돼 있지만 모델과 관련된 부가적인 논리 로직&lt;/b&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 컴포넌트에서 말하는 서비스도 위에서 알아본 Manager 와 동일하다
&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1. 저장소에서 데이터를 불러온다.&lt;/li&gt;
&lt;li&gt;2. 네트워크 호출 결과를 정리해서 객체에 넘겨준다.&lt;/li&gt;
&lt;li&gt;3. 저장소에 데이터를 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이러한 로직 모두 그 자체로 '연산'. 그래서 이를 도메인 객체에 넣기 어려움.&lt;/li&gt;
&lt;li&gt;애플리케이션에 꼭 필요하지만 '연산' 그 자체로는 어떠한 객체도 갖고 있기 애매해서 만들어지는 별도의 클래스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Service 또는 Manager임. -&amp;gt; 스프링 서비스 컴포넌트의 정체&lt;/b&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;서비스가 서비스를 호출한다? (Service-&amp;gt;Manager)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나는 스프링 컴포넌트를 이용해서 만든 ProductService&lt;/li&gt;
&lt;li&gt;하나는 계산 로직을 표현하기 위해 만든 PriceManager
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스가 서비스를 실행하고 있음&lt;/li&gt;
&lt;li&gt;양쪽 모두 어떤 도메인 객체로 표현하기 애매한 연산 로직을 모아둔 클래스이지만 성격은 다르다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PriceManager는 도메인 시스템을 구축&lt;/b&gt;하기 위해 존재&lt;/li&gt;
&lt;li&gt;가격을 계산한다는 점과 가격을 계산하는 &lt;b&gt;비즈니스 업무 규칙&lt;/b&gt;을 갖고 있으므로 '도메인'에 가까운 로직&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ProductService는 @Service 애너테이션으로 만들어진 ProductService는 &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;&lt;b&gt;어플리케이션의 실행에 초점에 맞춰 개발된 서비스&lt;/b&gt;&lt;/li&gt;
&lt;/ul&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;'라고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도메인 vs 도메인 서비스 vs 애플리케이션 서비스&lt;/b&gt;&lt;/h3&gt;
&lt;h5&gt;도메인 (도메인 로직)&lt;/h5&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비즈니스 로직을 처리&lt;/li&gt;
&lt;li&gt;도메인 역할을 수행&lt;/li&gt;
&lt;li&gt;다른 도메인과 협력&lt;/li&gt;
&lt;li&gt;ex) User, Product, Coupon&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;도메인 서비스 (도메인 연산)&lt;/h5&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: 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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ex) PriceManager&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;애플리케이션 서비스 (어플리케이션 연산)&lt;/h5&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: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인을 저장소에서 불러옴&lt;/li&gt;
&lt;li&gt;도메인 서비스를 실행&lt;/li&gt;
&lt;li&gt;도메인을 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ex) ProductService&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;@Service의 자바독 요약 2&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'J2EE의 비즈니스 서비스 파사드 패턴처럼 사용할 수 있다'&lt;/li&gt;
&lt;li&gt;-&amp;gt; == 도메인과, 도메인 서비스의 &lt;b&gt;파사드&lt;/b&gt;처럼 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도메인 VS 도메인 서비스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 도메인 서비스(ProductManager)는 도메인(Cashier)로 발전될 수 있음&lt;/li&gt;
&lt;li&gt;-&amp;gt; 도메인과 도메인 서비스를 구분짓는 것은 행동으로 결정..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;객제지향 개발자의 서비스 컴포넌트 조언&lt;/h2&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;li&gt;-&amp;gt; 서비스에 있는 비즈니스 로직을 도메인 객체로 옮겨라..!&lt;/li&gt;
&lt;li&gt;-&amp;gt; 서비스 코드를 작성할 때는 현재 작성 중인 코드가 '기존 도메인 객체에 들어갈 수는 없는지'&lt;/li&gt;
&lt;li&gt;-&amp;gt; 혹은 '새로운 도메인 모델로 만들수는 없는지' 고민&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 우선순위&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 모델 &amp;gt; 도메인 서비스 &amp;gt; 애플리케이션 서비스&lt;/li&gt;
&lt;li&gt;-&amp;gt; 테스트하기 쉽다&lt;/li&gt;
&lt;li&gt;-&amp;gt; 도메인 모델은 도메인 서비스보다 테스트하기 쉽고, 도메인 서비스는 애플리케이션 서비스보다 테스트하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;작은 기계 - 서비스 컴포넌트가 생성자 주입해야하는 주된 이유 -&amp;gt; 불변성&lt;/h2&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;-&amp;gt; '한 번 생성하면 여러 번 사용하지만 그 자신은 바꿀 수 없다. 생명주기도 매우 단순하다. 한 번 생성하면 특정 작업을 하는 작은 기계처럼 영원히 실행할 수 있다. 이러한 객체를 서비스라고 함'&lt;/li&gt;
&lt;li&gt;-&amp;gt; 불변성&lt;/li&gt;
&lt;li&gt;객체로 표현하기 애매한 연산 로직의 집합이 서비스&lt;/li&gt;
&lt;li&gt;서비스는 어떤 가변 상태를 갖는 객체가 아니라 '계산식'그 자체 -&amp;gt; 그러니 서비스는 불변.&lt;/li&gt;
&lt;li&gt;-&amp;gt; 생성자 주입&lt;/li&gt;
&lt;li&gt;-&amp;gt; 생성자 주입의 주된 이유는 서비스는 원래 불변하기 떄문.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서비스 컴포넌트를 위한 행동 조언&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스 멤버변수 모두 final&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.yes24.com/Product/Goods/126845564&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.yes24.com/Product/Goods/126845564&lt;/a&gt;&lt;/p&gt;</description>
      <category>컴퓨터 과학/[책] 자바스프링 개발자를 위한 실용주의 프로그래밍</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/206</guid>
      <comments>https://goalinnext.tistory.com/206#entry206comment</comments>
      <pubDate>Mon, 7 Oct 2024 19:45:21 +0900</pubDate>
    </item>
    <item>
      <title>학습일지 - 241006</title>
      <link>https://goalinnext.tistory.com/205</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발 방식&lt;br /&gt;-&amp;nbsp;거친&amp;nbsp;도메인&amp;nbsp;모델링&amp;nbsp;&amp;gt;&amp;nbsp;도메인&amp;nbsp;탐구(요구사항&amp;nbsp;파악)&amp;nbsp;&amp;gt;&amp;nbsp;시퀀스/상태&amp;nbsp;다이어그램&amp;nbsp;&amp;gt;&amp;nbsp;api&amp;nbsp;문서&amp;nbsp;(OAS&amp;nbsp;spec)&amp;nbsp;...&amp;nbsp;도메인&amp;nbsp;개발&amp;nbsp;&amp;lt;-&amp;gt;&amp;nbsp;API&amp;nbsp;개발&lt;br /&gt;&lt;span style=&quot;background-color: #171717; color: #ffffff; text-align: start;&quot;&gt;오늘도 여러분의 API는 안녕하신가요?&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://www.inflearn.com/course/lecture?courseSlug=%EC%9D%B8%ED%94%84%EC%BD%982023-%EB%8B%A4%EC%8B%9C%EB%B3%B4%EA%B8%B0&amp;amp;unitId=177892&amp;amp;tab=curriculum&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.inflearn.com/course/lecture?courseSlug=%EC%9D%B8%ED%94%84%EC%BD%982023-%EB%8B%A4%EC%8B%9C%EB%B3%B4%EA%B8%B0&amp;amp;unitId=177892&amp;amp;tab=curriculum&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;클린&amp;nbsp;아키텍처&amp;nbsp;구조에서&amp;nbsp;애플리케이션&amp;nbsp;레이어를&amp;nbsp;얇게,&amp;nbsp;프리젠테이션과&amp;nbsp;도메인&amp;nbsp;레이어를&amp;nbsp;두겁게&amp;nbsp;지향&lt;br /&gt;-&amp;nbsp;API&amp;nbsp;기능&amp;nbsp;빨리&amp;nbsp;만들어서&amp;nbsp;배포해보기&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;과제&amp;gt;&lt;br /&gt;1. 도메인 모델링 해오기&amp;nbsp;&lt;br /&gt;-&amp;nbsp;너무&amp;nbsp;만약에라는&amp;nbsp;가정을&amp;nbsp;너무&amp;nbsp;많이&amp;nbsp;하진&amp;nbsp;않기..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. AC 조건 (리스트 따로 ): 성공 조건 - 이 기능 만큼은 동작되어야 해.&lt;br /&gt;-&amp;nbsp;ex)&amp;nbsp;사용자는&amp;nbsp;자신이&amp;nbsp;원하는&amp;nbsp;블로그&amp;nbsp;RSS를&amp;nbsp;구독할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;(짧고&amp;nbsp;명확하게)&lt;br /&gt;-&amp;nbsp;아마도&amp;nbsp;충돌이&amp;nbsp;일어날&amp;nbsp;것.&amp;nbsp;-&amp;gt;&amp;nbsp;의견에&amp;nbsp;대한&amp;nbsp;(이걸&amp;nbsp;하면&amp;nbsp;좋을&amp;nbsp;것&amp;nbsp;같은데)&lt;br /&gt;-&amp;nbsp;이후에&amp;nbsp;도메인&amp;nbsp;모델링을&amp;nbsp;작성.&lt;br /&gt;- 의사소통의 목적이 크다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 요건이 변경되면 이 문서를 보고 작업한다라는 개념&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 비동기에 대한 기술 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 스토밍 (같이해보기) - 특정 도메인에서 발생되는 이벤트 및 정책&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기에&amp;nbsp;대한&amp;nbsp;내용&amp;nbsp;&lt;br /&gt;-&amp;nbsp;jdk(sync,&amp;nbsp;lock,&amp;nbsp;future,&amp;nbsp;flow)&lt;br /&gt;-&amp;nbsp;db락(비관,낙관)&lt;br /&gt;-&amp;nbsp;redis(pub/sub),&amp;nbsp;분산락..&amp;nbsp;&lt;br /&gt;&lt;br /&gt;비동기 기술 얘기&lt;br /&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;br /&gt;-&amp;nbsp;인풋&amp;nbsp;스트림&amp;nbsp;/&amp;nbsp;아웃풋&amp;nbsp;스트림&lt;br /&gt;&lt;br /&gt;알고리즘&lt;br /&gt;-&amp;nbsp;알고리즘&amp;nbsp;깃코드&amp;nbsp;-1,&amp;nbsp;2&amp;nbsp;레벨&lt;br /&gt;-&amp;nbsp;바킹독&amp;nbsp;실전&amp;nbsp;알고리즘.&amp;nbsp;-&amp;gt;&amp;nbsp;boj&amp;nbsp;문제집.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;꾸준하게&amp;nbsp;&lt;/p&gt;</description>
      <category>학습일지/학습일지</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/205</guid>
      <comments>https://goalinnext.tistory.com/205#entry205comment</comments>
      <pubDate>Sun, 6 Oct 2024 09:20:37 +0900</pubDate>
    </item>
    <item>
      <title>자바/스프링 개발자를 위한 실용주의 프로그래밍 5장 - 순환참조</title>
      <link>https://goalinnext.tistory.com/204</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;5장 순환참조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두개 이상의 객체나 컴포넌트가 서로를 참조함으로써 의존관계에 사이클이 생기는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;
@Data  
class Team {  
    private long id;    
    private String name;    
    private List&amp;lt;Member&amp;gt; members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀과 팀원의 관계를 표시한 클래스 -&amp;gt; 순환참조임&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;양방향 매핑&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JPA Entity의 양방향 매핑은 순환참조다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;순환참조의 문제점&lt;/h2&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;/h3&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;/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;/h3&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;&quot;팀내 모든 구성원의 월급을 합산해주세요 라는 요구사항이 있을 떄&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;
@Data  
class Team {  
    private long id;    
    private String name;    
    private List&amp;lt;Member&amp;gt; members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;  
    // 팀 내 모든 팀원의 월급의 합을 반환함  
    public int calculateTeamMemberTotalSalary() {
        int result = 0;
        for (Member member : myTeam.getMembers()) {
            result += member.getSalary();       
        }   
        return result;    
    }
}&lt;/code&gt;&lt;/pre&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;-&amp;gt; 잘못된 설계로 인해 이러한 &lt;b&gt;이상한 코드가 만들어질 수 있는 구조&lt;/b&gt;가 되는 것이 문제 !&lt;/li&gt;
&lt;li&gt;-&amp;gt; 의미상으로 그렇게 되어서는 안되는 코드는 컴파일 타임에 만들어지지 않게 하는 것이 좋다..&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 하위 객체에서 식별자를 통한 간접 참조로 변경  
@Data  
class Team {  
    private long id;   
    private String name;    
    private List&amp;lt;Member&amp;gt; members;
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private int myTeamId; // 애초에 이상한 개발을 하지 못하도록 ..
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 혹은 상위 객체에서 참조를 제거  
@Data  
class Team {  
    private long id;    
    private String name;//    
    private List&amp;lt;Member&amp;gt; members;  
}  

@Data  
class Member {  
    private long id;    
    private String name;    
    private Team myTeam;
}  

//&quot;팀내 모든 구성원의 월급을 합산해주세요 라는 요구사항이 있을 떄&quot;  
//요구사항은 서비스에서 구현  
@Service  
@RequriedArgsConstructor  
class TeamService {  
    private final MemberService memberService;  
    public long getTeamSalary(long id) {        
        return memberService.findByTeamId(memberId).steam()
        .mapToLong(Member::getSalary)
        .sum();    
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순환참조가 있으면 어떤 객체에 접근할 수 있는 접근 경로가 너무 많아짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;순환 참조를 해결하는 방법&lt;/h2&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;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;꼭 필요하지 않은 참조를 제거하거나 한쪽이 식별자를 갖게 하는 간접 참조로 바꾸기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;간접 참조 활용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;식별자를 갖고 있는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공통 컴포넌트 분리&lt;/h3&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;-&amp;gt; 공통 기능을 분리하는 과정에서 책임 분리가 적절하게 재조정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 기반 컴포넌트 사용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스를 공통 컴포넌트로 분리할 수 없다면 이벤트 기반 프로그래을 시스템에 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;양방향 매핑&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양방향 매핑은 순환참조 -&amp;gt; 왜 존재하는 것일까? 고민해보기&lt;/li&gt;
&lt;li&gt;-&amp;gt; 양방향 매핑은 어쩔수 없이 존재하는 도메인 문제에 최소한으로 적용하는게 바람직.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상위 수준의 순환참조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순환참조는 객체 뿐만아니라 패키지나 시스템 수준에서도 발생할 수 있는 문제임을 주의&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>컴퓨터 과학/[책] 자바스프링 개발자를 위한 실용주의 프로그래밍</category>
      <author>꾸준함의 미더덕</author>
      <guid isPermaLink="true">https://goalinnext.tistory.com/204</guid>
      <comments>https://goalinnext.tistory.com/204#entry204comment</comments>
      <pubDate>Tue, 24 Sep 2024 06:41:36 +0900</pubDate>
    </item>
  </channel>
</rss>